 
 
 
 Modules paramétrés
Les modules paramétrés sont aux modules ce que les fonctions sont
aux valeurs. Tout comme une fonction construit une nouvelle valeur à
partir de valeurs paramètres, un module paramétré 
permet de construire un nouveau module à partir de modules déjà
construits. On donne également le nom de foncteurs aux modules
paramétrés. 
L'introduction des foncteurs dans le langage des modules permet
d'accroître les possibilités de réutilisation du code défini dans
les structures.
Un foncteur se définit avec une syntaxe fonctionnelle :
 Syntaxe 
 
functor ( Nom : 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
Comme pour les fonctions, il existe un raccourci syntaxique à la
déclaration de foncteurs :
 Syntaxe 
 
module Nom1 ( Nom2 :
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
Un foncteur peut admettre plusieurs paramètres.
 Syntaxe 
 
| functor ( Nom1 : signature1 )  ->   : | 
| functor ( Nomn
: signaturen )  ->   structure | 
On peut, ici également, utiliser le raccourci syntaxique :
 Syntaxe 
 
| module Nom (Nom1 :
signature1 ) ...(
Nomn : signaturen ) = | 
| structure | 
L'application d'un foncteur à ses paramètres se fait selon le
schéma syntaxique suivant :
 Syntaxe 
 
module Nom = 
foncteur ( structure1 ) ...( structuren )
Notez que chaque paramètre est inscrit entre parenthèses.
Le résultat de l'application peut être un module simple ou, à
nouveau un foncteur, selon le nombre de paramètres réclamés par
le foncteur.
 Warning 
 
Il n'est pas possible de créer une signature par application d'une
signature << fonctorielle >> à d'autres signatures.
Un foncteur qui explicite complètement sa communication avec d'autres
modules, c'est-à-dire qui ne fait pas référence directement à un
élément d'un autre module hormis ses paramètres, est facilement
réutilisable. On peut effectivement l'appliquer à différents arguments
selon le cadre de son emploi. Il y a un parallèle entre une fonction
complètement paramétrée (sans variable libre) et un foncteur
complètement paramétré.
 Foncteurs et réutilisabilité
La distribution d'Objective CAML fournit trois modules définissant des
foncteurs. Deux d'entre eux prennent comme argument un module
fournissant un type de données totalement ordonné; c'est-à-dire
respectant la signature suivante : 
# module type OrderedType =
   sig
     type t
     val compare: t -> t -> int
   end ;;
module type OrderedType = sig type t val compare : t -> t -> int end
La fonction compare prend deux arguments et rend une valeur
négative si le premier est inférieur au second, une valeur nulle 
s'ils sont égaux, et une valeur positive si le premier est supérieur au
second. Voici un exemple de type ordonné.
# module OrderedIntPair = 
   struct  
     type t = int * int 
     let compare (x1,x2) (y1,y2) = let q = x1-y1 in if q=0 then x2-y2 else q 
   end ;;
module OrderedIntPair :
  sig type t = int * int val compare : int * int -> int * int -> int end
Le foncteur Make du module Map construit un module
qui implante des tables d'association ayant pour clés des valeurs du type
ordonné passé en argument. Ce module fournit des services
comparables à ceux des listes d'associations du module List
mais en utilisant une structure de données légèrement plus
complexe (des arbres binaires équilibrés). 
# 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
Le foncteur Make permet de construire des tables
d'associations ayant pour clé n'importe quel type de données dont
on saura écrire une fonction compare.
Le module Set fournit lui aussi un foncteur Make
prenant en argument un type ordonné et construisant un module
implantant les ensembles de valeurs de ce 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
Nous avons avec SetIntPair.t un type pour les ensembles de
couples d'entiers avec toutes les fonctions classiques sur les
ensembles. L'illustration de la réutilisabilité nous est donné
en construisant les ensembles d'ensembles de paires d'entiers.
# 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>
Le foncteur Make du module Hashtbl fonctionne de
manière similaire à celui du module Map mais en
construisant des tables de hachage à la place d'arbres
équilibrés. L'argument de ce foncteur est un peu différent; 
outre le type des clés de la table de hachage, il
doit fournir simplement une fonction testant l'égalité entre deux
clés à la place d'une fonction de comparaison. De plus,
il est nécessaire de donner la fonction de hachage, c'est-à-dire une
fonction associant un entier à une clé.
# 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
 Déclarations locales de modules 
Parmi les extensions du langage, il existe la possibilité de déclarer
localement un module.
 Syntaxe 
 
| let module Nom = structure | 
| in expr | 
Par exemple, nous souhaitons utiliser le module Set pour
écrire une fonction de tri sur les listes d'entiers en insérant 
chaque élément de la liste dans un ensemble et en récupérant
à la fin la liste des éléments de l'ensemble obtenu. 
# let sort l =
   let module M = 
      struct 
        type t = int  
        let compare = (-) 
      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 ne permet pas de faire sortir d'un module local une valeur
ayant un type qui n'est pas connu à l'extérieur de l'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
 
 
