OCAML Course

Functors

Go back

You can give modules as parameters to other modules. These modules are called functors. For instance, you could make a module implementing methods for a set, while not knowing if you are elements are int, strings, etc. This module ("set") will take in argument another module ("element type") describing the type of an element, which you will use when coding your module.

(* declaring a module having a type t.*)
module type SetElementType = sig
	type t
end

(* declaring a module describing a set *)
module type AbstractSet = sig
	(* types *)
	type elt
    type set
    (* functions *)
    val add : set -> elt -> set
	val empty : set
    val first : set -> elt
end

(*
As always, the set "GenericSet" will have to implements "AbstractSet" types and methods.

We are adding an argument that will have to be provided when creating the generic set: S.
And we are saying that the type of elt (=type of an element) will be equals to the type
stored in the type t of S.
*)
module GenericSet (S: SetElementType) : AbstractSet with type elt = S.t
(* copy-paste of the public parts aside from the functions and the modules that are implemented *)
module type SetElementType = sig
	type t
end

module type AbstractSet = sig
	(* types *)
	type elt
    type set
    (* functions *)
    val add : set -> elt -> set
	val empty : set
    val first : set -> elt
end

(*
	You do not know the type of the elt, aside from the fact
	that the function s will give you the type.

	You could use this complete definition, but I'm opting for the shorter one
	module GenericSet (S: SetElementType) : AbstractSet with type elt = S.t = struct
 *)
module GenericSet (S: SetElementType) = struct
	(* the type elt is equals to the type inside t *)
	type elt = S.t

	(* implement it hovewer you want *)
	type set = elt list
	let add set e = e::set
	let empty = []
    let first = List.hd
end
open Example

module Int_type = struct
	type t = int
end

module Int_Set = GenericSet(Int_type)
(* you could do that too, no need for a ; if you have multiple fields *)
module String_Set = GenericSet(struct type t = string end)

(* list with five *)
let set = Int_Set.add Int_Set.empty 5
let _ = Format.printf "%d@." (Int_Set.first set)

(* list with "five" *)
let set = String_Set.add String_Set.empty "five"
let _ = Format.printf "%s@." (String_Set.first set)

You can compile with these commands

$ ocamlc -c example.mli
$ ocamlc -c example.ml
$ ocamlc -c example_test.ml
$ ocamlc example.cmo example_test.cmo -o example_test
$ ./example_test
5
five