You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

6.4 KiB

OCaml

Small tutorials

Continuation Passing Style

In the discuss post What is the use of Continuation Passing Style (CPS)? I wrote a small explanation that I often link to people when they want to learn about CPS. I believe Jean-Christophe Filliâtre was the first one who told me about CPS and used this example to explain it to me. Here's a copy of my post.

I wrote a small (classical) example:

(* computing the length of a list, not tail-recursive *)
let rec list_length = function
  | [] -> 0
  | _::s -> 1 + list_length s (* not a tail call *)

(* tail-recursive version adding an accumulator *)
let list_length_tail l =
  let rec aux acc = function
  | [] -> acc
  | _::s -> aux (acc + 1) s
  in
  aux 0 l

type 'a binary_tree =
  | Empty
  | Node of 'a * ('a binary_tree) * ('a binary_tree)

(* computing the height of a tree, not tail-recursive *)
let rec tree_height = function
  | Empty -> 0
  | Node (_, l, r) -> 1 + max (tree_height l) (tree_height r)

(* really hard to make it tail-recursive by adding an accumulator, try it... *)

(* tail-recursive version using CPS *)
let tree_height_tail t =
  let rec aux t k = match t with
    | Empty -> k 0
    | Node (_, l, r) ->
        aux l (fun lh ->
        aux r (fun rh ->
        k (1 + max lh rh)))
  in
  aux t (fun x -> x)

let () =
  let l = [1; 2; 3; 4] in
  Format.printf "size of the list is: %d@." (list_length l);
  Format.printf "size of the list is: %d@." (list_length_tail l);
  let t = Node (1, Empty, Node(2, Node (3, Empty, Empty), Empty)) in
  Format.printf "height of the tree is: %d@." (tree_height t);
  Format.printf "height of the tree is: %d@." (tree_height_tail t)

Also note that getting from the non-CPS version to the CPS one is only a syntactic transformation:

let rec tree_height t = match t with
  | Empty -> 0
  | Node (_, l, r) -> 1 + max (tree_height l) (tree_height r)

(* add intermediate values: *)
let rec tree_height t = match t with
  | Empty -> 0
  | Node (_, l, r) ->
      let lh = tree_height l in
      let rh = tree_height r in
      1 + max lh rh

(* add a continuation to the args and before returning any value: *)
(* this is not valid OCaml *)
let rec tree_height t k = match t with
  | Empty -> k 0
  | Node (_, l, r) ->
      let lh = tree_height l in
      let rh = tree_height r in
      k (1 + max lh rh)

(* replace all intermediate `let x = f y` by `tree_height y (fun x ->` : *)
let rec tree_height t k = match t with
  | Empty -> k 0
  | Node (_, l, r) ->
      tree_height l (fun lh ->
      tree_height r (fun rh ->
      k (1 + max lh rh)))

GADT

In the discuss post Representing data more compactly but unsafely, Emile Trotignon gave the following example which I really like:

type a =
  | Int of int
  | Float of float

(* Safe but uses unnecessary boxing *)
let f_safe = function
  | Int i -> i
  | Float f -> int_of_float f

type b =
  | Int
  | Float

(* Very unsafe but no useless boxing *)
let f_unsafe tag n =
  match tag with
  | Int -> (Obj.magic n : int)
  | Float -> int_of_float (Obj.magic n : float)

type _ c =
  | Int : int c
  | Float : float c

(* Same runtime behaviour as f_unsafe but safe *)
let f_gadt : type t. t c -> t -> int =
 fun tag n ->
  match tag with
  | Int -> n
  | Float -> int_of_float n

Small riddles

How to write the val equal : unit -> unit -> bool function ?

When I teach OCaml to some people, after some time, I like to ask them this question. Here are the answer I usually get.

First answer: let equal x y = if x = y then true else false

Come on ! First you should write it let equal x y = x = y. But more importantly, there's no need to test for equality, there's only one inhabitant for the unit type, so it's always true. Try again !

Second answer: let equal x y = true

Well, dune will make the compiler shrill because of unused variables. Let's try again.

Third answer: let equal _ _ = true

Better, but this has the signature val equal : 'a -> 'b -> bool (and the previous answer too). How can we fix this ?

Fourth answer: let equal (_ : unit) (_ : unit) = true

OK. Now this is correct, but it's annoying we have to write the types. Unless...

Fifth answer: let equal () () = true

Yippee ! Usually the beginner is a little dumbfounded by this, as I was the first time I found this code in the stdlib. By the way, if you know a shortest way to write these (modulo whitespace changes), please tell me.

Then, what if you want to confuse the beginner completly ?

Sixth answer: let equal () = (=) ()

What's the shortest code producing the signature val f : 'a -> 'b ?

This question has been asked to me by Jean-Christophe Filliâtre while we were working on WebAssembly - don't ask me how we ended up talking about this. I quickly found a solution in 15 characters, but couldn't do better. He had one with 14 characters.

First solution in 20 characters: let f _=assert false1
Second solution in 15 characters: let rec f x=f x
Third solution in 15 characters: let f=Obj.magic1
Fourth solution in 14 characters: let f _=exit 1

I don't know any better solution, if you do please tell me !