Control structures in programming languages: from goto to algebraic effects

Xavier Leroy

Monads in OCaml (chapter 11)

(** The OCaml signature for a monad *)

module type MONAD = sig
  type 'a mon  (* the type of monadic computations producing a value of type 'a *)
  val ret: 'a -> 'a mon
  val bind: 'a mon -> ('a -> 'b mon) -> 'b mon
end

(** The nondeterminism monad (list monad) *)

module Nondet = struct
  type 'a mon = 'a list
  let ret x = [x]
  let rec bind m f =
    match m with
    | [] -> []
    | x :: m -> f x @ bind m f
  let choose (m: 'a mon) (n: 'a mon) : 'a mon = m @ n
  let fail : 'a mon = []
end

(** The exception monad *)

module Exception = struct
  type 'a mon = V of 'a | E of exn
  let ret x = V x
  let bind m f =
    match m with V x -> f x | E _ -> m
  let raise (e: exn) : 'a mon = E e
  let handle (m: 'a mon) (f: exn -> 'a mon) : 'a mon =
    match m with V _ -> m | E e -> f e
end

(** The continuation monad *)

module Continuation = struct
  type res = unit    (* the result type *)
  type 'a mon = ('a -> res) -> res
  let ret x = fun k -> k x
  let bind m f = fun k -> m (fun x -> f x k)
  type 'a cont = 'a -> res
  let callcc (f: 'a cont -> 'a mon) : 'a mon = fun k -> f k k
  let throw (k: 'a cont) (v: 'a) : 'b mon = fun k' -> k v
end

(** An implementation of a polymorphic store, for use with the state monad *)

module Store = struct
  module IMap = Map.Make(struct type t = int let compare = compare end)
  type value = ..
  type t = { nextkey: int; contents: value IMap.t }
  type 'a ref = {
    key: int;
    inj: 'a -> value;
    proj: value -> 'a
  }
  let empty = { nextkey = 0; contents = IMap.empty }
  let newref (type a) (v: a) (s: t) : a ref * t =
    let module M = struct type value += V of a end in
    let r =
      { key = s.nextkey;
        inj = (fun x -> M.V x);
        proj = (function M.V x -> x | _ -> assert false) } in
    let s' =
      { nextkey = s.nextkey + 1;
        contents = IMap.add r.key (r.inj v) s.contents } in
    (r, s')
  let get (r: 'a ref) (s: t) : 'a =
    r.proj (IMap.find r.key s.contents)
  let set (r: 'a ref) (v: 'a) (s: t) : t =
    { s with contents = IMap.add r.key (r.inj v) s.contents }
end

(** The state monad *)

module State = struct
  type 'a ref = 'a Store.ref
  type 'a mon = Store.t -> 'a * Store.t
  let ret x = fun s -> (x, s)
  let bind m f = fun s -> let (x, s1) = m s in f x s1
  let newref (v: 'a) : 'a ref mon = fun s -> Store.newref v s
  let get (r: 'a ref) : 'a mon = fun s -> (Store.get r s, s)
  let set (r: 'a ref) (v: 'a) : unit mon = fun s -> ((), Store.set r v s)
end

(** The reader monad (environment monad) *)

module Reader = struct
  type key = string   (* for example *)
  type value = int    (* for example *)
  type env = (key * value) list
  type 'a mon = env -> 'a
  let ret x = fun e -> x
  let bind m f = fun e -> f (m e) e
  let lookup (k: key) : value option mon = fun e -> List.assoc_opt k e
  let assign (k: key) (v: value) (m: 'a mon) : 'a mon = fun e -> m ((k, v) :: e)
end

(** A type of ropes, for use with the writer monad. *)

module Rope = struct
  type t = Zero | One of string | Two of t * t
  let empty = Zero
  let of_string s = One s
  let concat r1 r2 = Two(r1, r2)
  let extract (r: t) : string =
    let b = Buffer.create 100 in
    let rec extract r =
      match r with
      | Zero -> ()
      | One s -> Buffer.add_string b s
      | Two(r1, r2) -> extract r1; extract r2 in
    extract r; Buffer.contents b
end

(** The writer monad (output monad) *)

module Writer = struct
  type 'a mon = 'a * Rope.t
  let ret x = (x, Rope.empty)
  let bind m f =
    let (x, s1) = m in let (y, s2) = f x in (y, Rope.concat s1 s2)
  let print (s: string) : unit mon = ((), Rope.of_string s)
end

(** Probability monads *)

module Distribution = struct
  type 'a mon = ('a * float) list
  let ret x = [(x, 1.0)]
  let rec bind m f =
    match m with
    | [] -> []
    | (x, p) :: m -> List.map (fun (y, p') -> (y, p *. p')) (f x) @ bind m f
  let flip (p: float) : bool mon = [(true, p); (false, 1. -. p)]
end

module Expectation = struct
  type 'a mon = ('a -> float) -> float
  let ret x = fun k -> k x
  let bind m f = fun k -> m (fun x -> f x k)
  let flip (p: float) : bool mon = fun k -> p *. k true +. (1. -. p) *. k false
end

(** The undoable monad *)

module Undoable = struct
  type 'a mon = 'a * (unit -> unit)
  let ret x = (x, fun () -> ())
  let bind m f =
    let (x, undo1) = m in
    let (y, undo2) = f x in
    (y, (fun () -> undo2(); undo1()))
  let undo (m: 'a mon) : unit =
    let (_x, undo) = m in undo ()
  let ref_set (r: 'a ref) (new_v: 'a) : unit mon =
    let old_v = !r in r := new_v; ((), fun () -> r := old_v)
  let array_set (a: 'a array) (idx: int) (new_v: 'a) : unit mon =
    let old_v = a.(idx) in a.(idx) <- new_v; ((), fun () -> a.(idx) <- old_v)
end

(** The freer monad *)

type _ eff = ..

module Freer = struct
  type 'a mon =
    | Pure: 'a -> 'a mon
    | Op: 'b eff * ('b -> 'a mon) -> 'a mon
  let ret x = Pure x
  let rec bind m f =
    match m with
    | Pure x -> f x
    | Op(phi, k) -> Op(phi, fun x -> bind (k x) f)
  let perform (phi: 'b eff) : 'b mon =
    Op(phi, ret)
  type ('a, 'b) deep_handler =
    { retc: 'a -> 'b;
      effc: 'c. 'c eff -> (('c -> 'b) -> 'b) option }
  let rec fold (h: ('a, 'b) deep_handler) (m: 'a mon) : 'b =
    match m with
    | Pure x -> h.retc x
    | Op(phi, k) ->
        match h.effc phi with
        | Some g -> g (fun x -> fold h (k x))
        | None -> Op(phi, fun x -> fold h (k x))
  type ('a, 'b) shallow_handler =
    { retc: 'a -> 'b;
      effc: 'c. 'c eff -> (('c -> 'a mon) -> 'b) option }
  let case (h: ('a, 'b) shallow_handler) (m: 'a mon) : 'b =
    match m with
    | Pure x -> h.retc x
    | Op(phi, k) ->
        match h.effc phi with
        | Some g -> g k
        | None -> Op(phi, k)
end