OCaml
OCaml is a member of the ML family, developed at INRIA in France. Caml appeared in the 1980s, and Objective Caml (later OCaml) added objects in 1996. It is functional first, but it also has mutable state, loops and objects, and it compiles to fast native code. Its type system finds many errors before the program runs, while inferring nearly all types for you.
Paradigm: Functional Programming
What Makes OCaml Special
- Type inference. The compiler works out the type of every function and value from how they are used. You rarely write a type, yet the program is fully statically typed.
- Variants and pattern matching. A
typecan list its cases, each with data.matchtakes values apart, and the compiler warns when a case is missing. - Modules and functors. A module groups types and functions behind a signature. A functor is a function from modules to modules, so whole components can be parameterized.
- Functional first, imperative when useful. Data is immutable by default, but
refcells, arrays,forloops and exceptions are there when they are the simplest tool. - Options instead of null. A value that may be missing has type
option, and you must handleNonebefore you can use it. - Fast and predictable. The native compiler produces efficient code, and evaluation is strict (not lazy), so performance is easier to reason about than in Haskell.
- Multicore since OCaml 5. Version 5 (2022) added domains for parallelism and effect handlers for concurrency.
Example: Variants, pipelines, partial application and an option
type shape =
| Circle of float
| Rect of float * float
let area = function
| Circle r -> Float.pi *. r *. r
| Rect (w, h) -> w *. h
let shapes = [ Circle 1.0; Rect (2.0, 3.0) ]
(* a pipeline: each step feeds the next *)
let total = shapes |> List.map area |> List.fold_left ( +. ) 0.0
let () = Printf.printf "%.2f\n" total (* 9.14 *)
(* partial application: add 5 is a new function *)
let add x y = x + y
let add5 = add 5
let () =
List.iter (fun n -> Printf.printf "%d " (add5 n)) [ 1; 2; 3 ];
print_newline () (* 6 7 8 *)
(* option instead of null *)
let rec find_first p = function
| [] -> None
| x :: rest -> if p x then Some x else find_first p rest
let () =
match find_first (fun n -> n > 2) [ 1; 2; 3; 4 ] with
| Some n -> Printf.printf "found %d\n" n (* found 3 *)
| None -> print_endline "none"
(* imperative style, when it is simplest *)
let counter = ref 0
let () =
for i = 1 to 3 do counter := !counter + i done;
Printf.printf "%d\n" !counter (* 6 *)
How It Works
areais defined withfunction, which is amatchon its argument. Its type,shape -> float, is inferred. Note the dotted operators*.and+.for floats, since OCaml never converts numbers silently.|>passes the value on its left as the last argument to the function on its right, so the pipeline reads in the order it runs.add 5gives back a function that waits fory. Every OCaml function takes one argument at a time (it is curried).find_firstreturnsSome xorNone. The caller must match on both, so a forgotten check is a warning, not a crash.- The last block shows that OCaml also allows mutation:
refcreates a cell,!reads it and:=writes it.
History and Where It Is Used
OCaml is used for compilers and language tools, theorem provers (Coq, now Rocq, is written in it), static analyzers (Flow and Infer from Facebook), and in finance, notably at Jane Street. It is also common in teaching programming languages. The standard tools are dune (build) and opam (packages). The ML family also includes F#, which brings much of the OCaml style to .NET.