Skip to content

Instantly share code, notes, and snippets.

@jtpaasch
Created January 14, 2019 13:24
Show Gist options
  • Select an option

  • Save jtpaasch/ca52e9ad795e763aaa4958247e195543 to your computer and use it in GitHub Desktop.

Select an option

Save jtpaasch/ca52e9ad795e763aaa4958247e195543 to your computer and use it in GitHub Desktop.
Functors in OCaml
(*********************************************************)
(** Simple functor example. *)
(** A simple signature. *)
module type X = sig
val x : int
end
(** Here's a fuctor.
* It takes an input: [M], which is an implementation
* of type [X].
* It produces an output, namely the structure defined
* in the body of the [struct]. *)
module IncX (M : X) = struct
let x = M.x + 1
end
(* A functor is a parameterized structure. Here, [M] is the param.
* The structure it produces depends on the parameter, [M]. *)
(** Here's a simple module that matches the type [X]. *)
module A = struct
let x = 0
end
(* [A.x] is [0:int]. *)
(** Let's build an [IncX] module, using [A] as input. *)
module B = IncX(A)
(* What's the value of [B.x]? It's [1:int]. Why?
* Because [IncX.x] is [M.x + 1], and in this case,
* [M] is [A], so it's [A.x + 1] => [0 + 1] => [1]. *)
(** We can build another one on top of that. *)
module C = IncX(B)
(** What's the value of [C.x]? It's [2:int], because
* [C.x] is [M.x + 1] and [M] is [B]. So that's
* [B.x + 1], which is [1 + 1] => [2]. *)
(** A functor needn't care about its input.
* Here's one that ignores the input altogether. *)
module MakeY (M : X) = struct
let y = 42
end
(*********************************************************)
(** Alternative syntax. *)
(** The following two syntaxes are equivalent. *)
module F (M : X) = struct end
module F = functor (M : X) -> struct end
(* The second one uses the [functor] keyword, so it's like
* an anonymous function built with the [fun] keyword. *)
(*********************************************************)
(** Multiple parameters. *)
(** Here's another simple module signature. *)
module type Y = sig
val y : int
end
(** Functors can have multiple parameters. *)
module IncXY (M : X) (N : Y) = struct
let x = M.x + 1
let y = N.y + 2
end
(** Here's an implementation of [Y]. *)
module D = struct
let y = 100
end
(** Let's create a structure using [IncXY]. *)
module E = IncXY(A)(D)
(** What's the value of [E.x]? It's [1:int], because [A.x] is [0],
* plus [1], makes [1].
* What's the value of [E.y]? it's [102:int], because [D.y] is [100],
* plus [2], makes [102]. *)
(*********************************************************)
(** Using standard library functors. *)
(** [Map]s can have any kind of key. You need to tell it
* what kind of key you want, and you need to tell it
* how to determine if one is prior to another in order. *)
(** The [Map.Make] functor requires that you give it an [OrderedType]
* module as a parameter. That requires that you specify a type [t]
* and a [compare] function. We can make one easy: *)
module IntOrderedType = struct
type t = int
let compare = Pervasives.compare
end
(** Now we can instantiate a [Map], with [Int]s for keys. *)
module IntMap = Map.Make(IntOrderedType)
(** An empty [IntMap]. *)
let m0 = IntMap.empty;;
(** Add the value ["one"] with key [1]. *)
let m1 = IntMap.add 1 "one" m0;;
(** Find the value for key [1]. *)
IntMap.find 1 m1;;
(** Check if a key is in a map. *)
IntMap.mem 1 m1;;
IntMap.mem 42 m1;;
(** See all mapped items. *)
IntMap.bindings m1;;
(** The [String] module already has a type [t] = [string]
* and a [compare] function. So we can make a map with
* [String] keys too. *)
module StringMap = Map.Make(String)
let s0 = StringMap.empty;;
let s1 = StringMap.add "one" 1 s0;;
StringMap.find "one" s1;;
StringMap.mem "one" s1;;
StringMap.mem "two" s1;;
let s2 = StringMap.add "three" 3 s1;;
StringMap.bindings s2;;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment