Project setup
opamis the package manager.duneto OCaml is like sbt to Scala.utopis the REPL.merlinprovides IDE features for your editor.ocp-indenthelps you indent OCaml code.ocamlformathelps you format OCaml code.
# setup opam
opam init
# install OCaml
opam switch create 4.10.0
# update environment
eval $(opam env)
# install packages
opam install dune utop merlin ocp-indent ocamlformat
If you just need to meddle with a single file, create a dune file along with it and you’re good to go:
(executable
(name main)
(libraries base other_lib))
main is nothing but the name correspond to the file main.ml, or whatever you file name is. When you run dune build main.exe it’ll generate an executable inside ./_build/default/main.exe which you can run with. Or you can use dune exec ./main.exe which combines building and running an executable into a single operation. Use opam to install whatever libraries you need and add them to libraries stanza.
Now to work with multiple files, read the dune document first. This is an example:
.:
bin dune-project my-proj.opam src
./bin:
dune main.ml
./src:
ast.ml dune lexer.mll other.ml parser.mly
; file: dune-project
(lang dune 2.6)
(name my-proj)
; you can ignore this.
(using menhir 2.1)
; file: ./bin/dune
(executable
(name main)
(libraries lib))
; file: ./src/dune
(library
(name lib))
; you can ignor this.
(ocamllex lexer)
(menhir (modules parser))
Note that if you want to run dune utop and automatically load modules, it has to be a library. So I setup main.ml as an entry point and keep my source files is src, aka a library.
Emacs setup
You can check out my setup.
- use
C-c C-sto switch between source file and REPL. lain/ocaml-utopwill ask which directory you want to run the REPL in, default is the project root.- format the project with
, d Fand many other commands in, d. - eval current expression with
, e eand many other commands in, e. - goto definition with
M-.and go back withM-,.
OCaml
OCaml doesn’t have a function composition operator like the Haskell .. Here is a discussion, TLDR: OCaml doesn’t favor point-free style.
OCaml doesn’t have ad hoc polymorphism, it’s the reason you have print_string and print_int instead of a single print. You may know this as overloading in other languages. Modular implicits is OCaml’s way to support it, but it seems not under active development. Here is a discussion, TLDR: Multicore is more important.
Haskell’s functor and OCaml’s functor are not the same thing, although they both got the name from Category Theory. OCaml’s functor works on module. In short it’s a parametrized module which is a module that takes another module(s) as argument(s) and outputs a module.
You may already know that modules took an important role in OCaml. They’re not like other languages where they exists just to separate namespaces or import namespaces. OCaml’s module system provides abstraction and composition. It also makes compilation very fast because it ensure that each module can be typechecked and compiled incrementally.
Java’s Generic and Interfaces provides something similar to OCaml module system, here is an example of OCaml generic Set.
module type OrderedType = sig
type t
val compare : t -> t -> int
end
module IntType = struct
type t = int
let compare x y = x - y
end
module IntSet = Set.Make(IntType)
(* And since OCaml's Int module already have these, you can just use it *)
module IntSet' = Set.Make(Int)
However Java doesn’t support higher kinded polymorphism, simply put, you can only abstract over type(Set
module Tree (F : sig type 'a t end) = struct
type 'a t = Leaf of 'a | Node of ('a t) F.t
end
(* And here we can create Rose Tree or Binary Tree *)
module RoseTree = Tree (struct type 'a t = 'a list end)
module BinTree = Tree (struct type 'a t = 'a * 'a end)
And if you want to get your hands dirty with front end, there is ReasonML.
That’s about it, here are some helpful resources: