Benefits of OCaml

Module system


Like objects, OCaml's module system provides a way to encapsulate related definitions. However, modules are more static in nature and, in particular, make better use of static type checking.

Module interfaces are called signatures and bodies are called structures.

A module signature is defined like this:

# module type INT = sig
    type t = int
    val compare : t -> t -> int
module type INT = sig type t val compare : t -> t -> int end

A module structure adhering to the signature is defined like this:

# module Int : INT = struct
    type t = int
    let compare i j = if i<j then -1 else if i=j then 0 else 1

Modules may be "derived" from other modules using the include keyword. For example, the following replaces the built-in List module with a new module that implements an additional function count:

# module List = struct
    include List

    let rec count pred list =
      List.fold_left (fun n x -> n + if pred x then 1 else 0) 0 list
module List :
    val length : 'a list -> int
    val hd : 'a list -> 'a
    val tl : 'a list -> 'a list

In the presence of an expressive static type system like OCaml's, modules are a preferable alternative to objects, improving error reporting, accelerating development time and improving reliability.


Functors map modules onto modules. This allows a set of related definitions to be used to construct definitions that depend upon them. For example, the Int module defined above can be fed to the Set.Make functor to create a module IntSet that represents sets of integers:

# module IntSet = Set.Make(Int);;
module IntSet :
    type elt = Int.t
    type t = Set.Make(Int).t
    val empty : t
    val is_empty : t -> bool
    val mem : elt -> t -> bool
    val add : elt -> t -> t

Sets that use different comparison functions are regarded as separate types by OCaml. The compiler then ensures that sets defined using one comparison function cannot be accidentally used in place of sets that use a different comparison function.

Modules and functors allow assurances to be enforced by the compiler at compile time, improving reliability and accelerating development.