Author: Christian.Queinnec@lip6.fr

## 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