Lab · Playground
OCaml playground
The OCaml 5 toplevel in your browser — type inference, pattern matching, variants, records, and modules with functors.
OCaml is the practical branch of the ML family, the languages Robin Milner’s group built in the 1970s as the meta-language of a theorem prover. It arrived from INRIA in 1996 and is still where people go when they want a strong static type system without writing the types down: the Rust compiler was first written in it, Jane Street runs a trading firm on it, and the Coq/Rocq prover and the Flow and Hack type checkers are built with it.
These blocks run the OCaml 5 toplevel compiled to JavaScript with js_of_ocaml
(@live-codes/ocaml), in a Web Worker; the first Run downloads
about 5 MB. Each run starts a fresh toplevel. Anything you print appears as usual; a block that prints
nothing shows the toplevel’s reply instead — each definition’s type and value, as in utop.
let compose f g x = f (g x)
let twice f = compose f f
let add n x = x + n
let shout s = String.uppercase_ascii s ^ "!"
let eleven_more = twice (add 10) 1
let loud = twice shout "hey"
let pairs = List.combine [1; 2; 3] ["one"; "two"; "three"]
Nothing is printed, so you see what the toplevel inferred for each definition. compose gets the
most general type there is, ('a → 'b) → ('c → 'a) → 'c → 'b: the 'a`s are type variables,
so the same function works on integers and strings with no generics syntax. Add a
`let () = print_endline loud and the toplevel’s reply gives way to your output.
let rec pairs = function
| a :: (b :: _ as rest) -> (a, b) :: pairs rest
| _ -> []
let rec run_length = function
| [] -> []
| x :: rest ->
match run_length rest with
| (y, n) :: tail when y = x -> (x, n + 1) :: tail
| encoded -> (x, 1) :: encoded
let () =
pairs [1; 2; 3; 4]
|> List.iter (fun (a, b) -> Printf.printf "(%d,%d) " a b);
print_newline ();
run_length (List.of_seq (String.to_seq "aaabccddddde"))
|> List.iter (fun (c, n) -> Printf.printf "%c%d " c n);
print_newline ()
type expr =
| Num of float
| Var of string
| Add of expr * expr
| Mul of expr * expr
| Pow of expr * int
let rec eval env = function
| Num n -> n
| Var v -> List.assoc v env
| Add (a, b) -> eval env a +. eval env b
| Mul (a, b) -> eval env a *. eval env b
| Pow (a, n) -> eval env a ** float_of_int n
(* Symbolic differentiation is just more pattern matching *)
let rec deriv x = function
| Num _ -> Num 0.
| Var v -> Num (if v = x then 1. else 0.)
| Add (a, b) -> Add (deriv x a, deriv x b)
| Mul (a, b) -> Add (Mul (deriv x a, b), Mul (a, deriv x b))
| Pow (a, n) -> Mul (Mul (Num (float_of_int n), Pow (a, n - 1)), deriv x a)
let rec show = function
| Num n -> Printf.sprintf "%g" n
| Var v -> v
| Add (a, b) -> Printf.sprintf "(%s + %s)" (show a) (show b)
| Mul (a, b) -> Printf.sprintf "%s·%s" (show a) (show b)
| Pow (a, n) -> Printf.sprintf "%s^%d" (show a) n
let f = Add (Mul (Num 3., Pow (Var "x", 2)), Mul (Num 2., Var "x")) (* 3x² + 2x *)
let () =
Printf.printf "f = %s\n" (show f);
Printf.printf "f(2) = %g\n" (eval [ ("x", 2.) ] f);
Printf.printf "f' = %s\n" (show (deriv "x" f));
Printf.printf "f'(2) = %g\n" (eval [ ("x", 2.) ] (deriv "x" f))
Delete the Pow case from deriv and the OCaml compiler warns that the match is no longer
exhaustive, naming the case you forgot — most of the reason people love variants. (This toplevel
doesn’t show warnings; run it and you get the Match_failure the warning was about.)
type book = { title : string; author : string; year : int; isbn : string option }
let shelf = [
{ title = "SICP"; author = "Abelson & Sussman"; year = 1985; isbn = Some "0262010771" };
{ title = "The Art of Computer Programming"; author = "Knuth"; year = 1968; isbn = None };
{ title = "Real World OCaml"; author = "Minsky, Madhavapeddy"; year = 2013; isbn = Some "144932391X" };
]
let describe b =
let isbn = match b.isbn with Some i -> "ISBN " ^ i | None -> "no ISBN" in
Printf.printf "%-34s %4d %s\n" b.title b.year isbn
let () = List.iter describe shelf
(* Functional update: a copy with one field changed *)
let second_ed = { (List.hd shelf) with year = 1996 }
let () = describe second_ed
let oldest = List.fold_left (fun acc b -> if b.year < acc.year then b else acc) (List.hd shelf) shelf
let () = print_endline ("oldest: " ^ oldest.author)
let find_isbn t = List.find_opt (fun b -> b.title = t) shelf |> Option.map (fun b -> b.isbn) |> Option.join
let () =
[ "SICP"; "The Art of Computer Programming"; "Dune" ]
|> List.iter (fun t -> print_endline (t ^ ": " ^ Option.value (find_isbn t) ~default:"?"))
(* A module type is an interface; a functor is a function from modules to modules *)
module CaseInsensitive = struct
type t = string
let compare a b = String.compare (String.lowercase_ascii a) (String.lowercase_ascii b)
end
module WordCount = Map.Make (CaseInsensitive)
let count text =
String.split_on_char ' ' text
|> List.filter (( <> ) "")
|> List.fold_left
(fun m w -> WordCount.update w (function None -> Some 1 | Some n -> Some (n + 1)) m)
WordCount.empty
let () =
count "The cat and THE hat and the bat"
|> WordCount.iter (fun w n -> Printf.printf "%s=%d " w n);
print_newline ()
(* Your own signature, and a module that hides its representation behind it *)
module type STACK = sig
type 'a t
val empty : 'a t
val push : 'a -> 'a t -> 'a t
val pop : 'a t -> ('a * 'a t) option
end
module Stack : STACK = struct
type 'a t = 'a list
let empty = []
let push x s = x :: s
let pop = function [] -> None | x :: s -> Some (x, s)
end
let () =
let s = Stack.(empty |> push 1 |> push 2 |> push 3) in
match Stack.pop s with
| Some (top, _) -> Printf.printf "top is %d\n" top
| None -> print_endline "empty"
let parse_int s =
match int_of_string_opt (String.trim s) with
| Some n -> Ok n
| None -> Error (Printf.sprintf "%S is not a number" s)
let positive n = if n > 0 then Ok n else Error (Printf.sprintf "%d is not positive" n)
let ( let* ) = Result.bind
let ratio a b =
let* x = parse_int a in
let* y = parse_int b in
let* y = positive y in
Ok (float_of_int x /. float_of_int y)
let () =
[ ("22", "7"); ("10", "zero"); ("5", "-2") ]
|> List.iter (fun (a, b) ->
match ratio a b with
| Ok r -> Printf.printf "%s/%s = %.4f\n" a b r
| Error e -> Printf.printf "%s/%s: %s\n" a b e)