 
 
 
 Parameterized Modules
Parameterized modules are to modules what functions are to base
values. Just like a function returns a new value from the values of
its parameters, a parameterized module builds a new module from the
modules given as parameters. Parameterized modules are also called
functors.
The addition of functors to the module language increases the
opportunities for code reuse in structures.
Functors are defined using a function-like syntax:
 Syntax 
 
functor ( Name : signature )
  -> structure
# module Couple = functor ( Q : sig type t end ) -> 
   struct type couple = Q.t * Q.t end ;;
module Couple :
  functor(Q : sig type t end) -> sig type couple = Q.t * Q.t end
As for functions, syntactic sugar is provided for defining and naming
a functor:
 Syntax 
 
module Name1 ( Name2 :
signature ) = structure 
# module Couple ( Q : sig type t end ) =  struct type couple = Q.t * Q.t end ;;
module Couple :
  functor(Q : sig type t end) -> sig type couple = Q.t * Q.t end
A functor can take several parameters:
 Syntax 
 
| functor ( Name1 : signature1 )  ->   : | 
| functor ( Namen
: signaturen )  ->   structure | 
The syntactic sugar for defining and naming a functor extends to
multiple-argument functors:
 Syntax 
 
| module Name (Name1 :
signature1 ) ...(
Namen : signaturen ) = | 
| structure | 
The application of a functor to its arguments is written thus:
 Syntax 
 
module Name = 
functor ( structure1 ) ...( structuren )
Note that each parameter is written between parentheses. The result
of the application can be either a simple module or a partially
applied functor, depending on the number of parameters of the functor.
 Warning 
 
There is no equivalent to functors at the level of signature: it is
not possible to build a signature by application of a ``functorial
signature'' to other signatures.
A closed functor is a functor that does not reference any module
except its parameters. Such a closed functor makes its communications
with other modules entirely explicit. This provides maximal
reusability, since the modules it references are determined at
application time only. There is a strong parallel between a closed
function (without free variables) and a closed functor.
 Functors and Code Reuse
The Objective CAML standard library provides three modules defining
functors. Two of them take as argument a module implementing a
totally ordered data type, that is, a module with the following signature:
# module type OrderedType =
   sig
     type t
     val compare: t -> t -> int
   end ;;
module type OrderedType = sig type t val compare : t -> t -> int end
Function compare takes two arguments of type t
and returns a negative integer if the first is less than the second,
zero if both are equal, and a positive integer if the first is greater
than the second. Here is an example of totally ordered type: pairs of
integers equipped with lexicographic ordering.
# module OrderedIntPair = 
   struct  
     type t = int * int 
     let compare (x1,x2) (y1,y2) =
       if x1 < y1 then -1
       else if x1 > y1 then 1
       else if x2 < y2 then -1
       else if x2 > y2 then 1
       else 0
   end ;;
module OrderedIntPair :
  sig type t = int * int val compare : 'a * 'b -> 'a * 'b -> int end
The functor Make from module Map returns a module
that implements association tables whose keys are values of the
ordered type passed as argument. This module provides operations
similar to the operations on association lists from module
List, but using a more efficient and more complex data
structure (balanced binary trees).
# module AssocIntPair = Map.Make (OrderedIntPair) ;;
module AssocIntPair :
  sig
    type key = OrderedIntPair.t
    and 'a t = 'a Map.Make(OrderedIntPair).t
    val empty : 'a t
    val add : key -> 'a -> 'a t -> 'a t
    val find : key -> 'a t -> 'a
    val remove : key -> 'a t -> 'a t
    val mem : key -> 'a t -> bool
    val iter : (key -> 'a -> unit) -> 'a t -> unit
    val map : ('a -> 'b) -> 'a t -> 'b t
    val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
  end
The Make functor allows to construct association tables over
any key type for which we can write a compare function.
The standard library module Set also provides a functor named
Make taking an ordered type as argument and returning a
module implementing sets of sets of values of this type.
# module SetIntPair = Set.Make (OrderedIntPair) ;;
module SetIntPair :
  sig
    type elt = OrderedIntPair.t
    and t = Set.Make(OrderedIntPair).t
    val empty : t
    val is_empty : t -> bool
    val mem : elt -> t -> bool
    val add : elt -> t -> t
    val singleton : elt -> t
    val remove : elt -> t -> t
    val union : t -> t -> t
    val inter : t -> t -> t
    val diff : t -> t -> t
    val compare : t -> t -> int
    val equal : t -> t -> bool
    val subset : t -> t -> bool
    val iter : (elt -> unit) -> t -> unit
    val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a
    val cardinal : t -> int
    val elements : t -> elt list
    val min_elt : t -> elt
    val max_elt : t -> elt
    val choose : t -> elt
  end
The type SetIntPair.t is the type of sets of integer pairs,
with all the usual set operations provided in SetIntPair,
including a set comparison function SetIntPair.compare.
To illustrate the code reuse made possible by functors, we now build
sets of sets of integer pairs.
# module SetofSet = Set.Make (SetIntPair) ;;
# let x = SetIntPair.singleton (1,2) ;;           (* x = { (1,2) }        *)
val x : SetIntPair.t = <abstr>
# let y = SetofSet.singleton SetIntPair.empty ;;  (* y = { {} }           *)
val y : SetofSet.t = <abstr>
# let z = SetofSet.add x y ;;                     (* z = { {(1,2)} ; {} } *)
val z : SetofSet.t = <abstr>
The Make functor from module Hashtbl is similar to
that from the Map module, but implements (imperative) hash
tables instead of (purely functional) balanced trees. The argument to
Hashtbl.Make is slightly different: in addition to the type
of the keys for the hash table, it must provide an equality function
testing the equality of two keys (instead of a full-fledged comparison
function), plus a hash function, that is, a function associating
integers to keys.
# module type HashedType =
   sig
     type t
     val equal: t -> t -> bool
     val hash: t -> int
   end ;;
module type HashedType =
  sig type t val equal : t -> t -> bool val hash : t -> int end
# module IntMod13 = 
   struct 
     type t = int 
     let equal = (=) 
     let hash x = x mod 13 
   end ;;
module IntMod13 :
  sig type t = int val equal : 'a -> 'a -> bool val hash : int -> int end
# module TblInt = Hashtbl.Make (IntMod13) ;;
module TblInt :
  sig
    type key = IntMod13.t
    and 'a t = 'a Hashtbl.Make(IntMod13).t
    val create : int -> 'a t
    val clear : 'a t -> unit
    val add : 'a t -> key -> 'a -> unit
    val remove : 'a t -> key -> unit
    val find : 'a t -> key -> 'a
    val find_all : 'a t -> key -> 'a list
    val mem : 'a t -> key -> bool
    val iter : (key -> 'a -> unit) -> 'a t -> unit
  end
 Local Module Definitions
The Objective CAML core language allows a module to be defined locally to an
expression.
 Syntax 
 
| let module Name = structure | 
| in expr | 
For instance, we can use the Set module locally to write a
sort function over integer lists, by inserting each list element into a
set and finally converting the set to the sorted list of its elements.
# let sort l =
   let module M = 
      struct 
        type t = int  
        let compare x y =
          if x < y then -1 else if x > y then 1 else 0
      end 
  in
    let module MSet = Set.Make(M) 
    in  MSet.elements (List.fold_right MSet.add l MSet.empty) ;;
val sort : int list -> int list = <fun>
# sort [ 5 ; 3 ; 8 ; 7 ; 2 ; 6 ; 1 ; 4 ] ;; 
- : int list = [1; 2; 3; 4; 5; 6; 7; 8]
Objective CAML does not allow a value to escape a let module
expression if the type of the value is not known outside the scope
of the expression.
# let test = 
   let module Foo = 
     struct 
       type t 
       let id x = (x:t)
     end
   in Foo.id ;;
Characters 15-101:
This `let module' expression has type Foo.t -> Foo.t
In this type, the locally bound module name Foo escapes its scope
 
 
