OCAML Course

Modules

Go back

We can group declarations of functions and new types in a container, called a module. This is a sort of class, if you are familiar with this notion. The declarations of functions/types inside the modules are described in a module type, and your module will take the type of a module type.

If you are familiar with Java, a module type is an interface, and a module is a class implementing the interface. You can have many modules implementing one interface, but every module can only implement one module type.

(* we are declaring that every module having this type,
 will have to define the function add, and will have a type called set.*)
module type IntSetListType = sig
	(* Others files can use the function add or the type set *)

    type set = int list
    (* a set is a list of ints *)

    val add : set -> int -> set
    (** [add s e]: take a set, an int, and return the set with the new element inside. *)
end

(* The set is an implementation of IntSetListType with ordered and unique values *)
module OrderedUniqueIntSetList : IntSetListType
(* copy paste of the public parts aside the functions and the modules that are implemented *)
module type IntSetListType = sig
    type set = int list

    val add : set -> int -> set
end

module OrderedUniqueIntSetList = struct
	type set = int list

	(* this is private *)
	exception Exit_add

	(** "override add" *)
	let add set e = try
		let rec add_acc s = match s with
			| [] -> [e]
			| hd::tl ->
				if (e = hd) then raise Exit_add
	            else if (hd > e) then hd::(add_acc tl) (* head unchanged, checking the rest *)
	            else e::s (* add first, as it is the lowest value *)
	    in add_acc set
	with Exit_add -> set (* using exceptions to exit faster, and return the unchanged list *)
end
open Example

(* renaming, not required at all, you could use OrderedUniqueIntSetList *)
module OL = OrderedUniqueIntSetList

(* list with five *)
let five = OL.add [] 5
(* print the first value *)
let _ = Format.printf "%d@." (List.hd five)

You can compile with these commands

$ ocamlc -c example.mli
$ ocamlc -c example.ml
$ ocamlc -c example_test.ml
# creating executable
$ ocamlc example.cmo example_test.cmo -o example_test
$ ./example_test
5 # ok

Unfortunately, this code is not very good. The one using my interface knows that I'm using lists, so I would not be able to change it. Here is a more generic version.

(*
Every module having this type will have to define
what are elt, what is set, and what's the code for add.
*)
module type AbstractIntSet = sig
	type elt = int (* for now, this is still inside the mli *)
    type set (* type up to the implementation *)

    val add : set -> elt -> set
    (** [add s e]: take a set, an element, and return the set with the new element inside. *)

	val empty : set
    val first : set -> elt
    (** [first s]: returns the first element *)
end

(* The IntSet is an implementation of AbstractSet *)
module IntSet : AbstractIntSet
(* copy paste of the public parts aside the functions and the modules that are implemented *)
module type AbstractIntSet = sig
	type elt = int
    type set
    val add : set -> elt -> set
    val empty : set
	val first : set -> elt
end

(* in my implementation, called IntSet, I'm using a list,
 with ordered and unique values. Aside from the one reading this code, no one else knows.
 *)
module IntSet : AbstractIntSet = struct
	type elt = int
	type set = elt list

	(* this is private *)
	exception Exit_add

	(** "override add" *)
	let add set e = try
		let rec add_acc s = match s with
			| [] -> [e]
			| hd::tl ->
				if (e = hd) then raise Exit_add
	            else if (hd > e) then hd::(add_acc tl) (* head unchanged, checking the rest *)
	            else e::s (* add first, as it is the lowest value *)
	    in add_acc set
	with Exit_add -> set (* using exceptions to exit faster, and return the unchanged list *)

	let first = List.hd
	let empty = []
end
open Example

(* list with five *)
let set = IntSet.add IntSet.empty 5
(* print the first value *)
let _ = Format.printf "%d@." (IntSet.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 # ok