• [^] # Re: Performance

    Posté par . En réponse au journal Moi, expert C++, j'abandonne le C++. Évalué à 2.

    J'ai répondu un peu vite hier, mais plus qu'un type paramétré, ton histoire ressemble à une classe paramétrée par d'autres classes. Dans le jargon des langages ML, on appelle cela des foncteurs : des modules paramétrés par d'autres modules, c'est-à-dire des fonctions des modules dans les modules. Les types paramétriques sont des foncteurs, mais tous les foncteurs de sont pas des types paramétriques (comme les canards sont des animaux, mais les animaux ne sont pas tous des canards).

    Pour comprendre un peu ce qu'est un module, prenons cette classe python :

    class a:
     def __init__(self, i):
     self.attr = i
     def get_attr(self):
     return self.attr

    Dans les langages de la famille ML, on n'utilisera pas de classe pour exprimer cela mais un module :

    module A : sig 
     type 'a t
     val init : 'a -> 'a t
     val get_attr : 'a t -> 'a
    end = struct
     type 'a t = 'a
     let init i = i
     let get_attr x = x
    end

    Le problème général du paradigme objet est qu'il confond (de manière totalement ridicule) un concept avec une théorie sur un concept donné. Ici le concept (qui est générique) c'est le type paramétrique A.t et les autres fonctions du modules forment une théorie sur ce concept. Du point du vue du paradigme objet, on se met à dire que c'est la théorie sur A.t, soit le module A, qui définit le concept : c'est inepte. On peut tout à fait étendre la théorie (rajouter des fonctions) sans changer le moins du monde le concept dont elle parle.

    Là où cela devient gênant en général, c'est quand on pratique l'héritage. On a coutume de dire, dans le monde de l'orienté objet, que comme un canard est un animal alors la classe canard hérite de la classe animale : c'est faux et absolument faux. La proposition les canards sont des animaux signifie que les canards sont un sous-type (et non une sous-classe) du type animal. Il est rare que des les deux notions de sous-classe et de sous-type se recouvre, en particulier ça ne marche plus dès qu'il y a des opérateurs binaires.

    Quittons cette digression sur le monde objet, et revenons à nos modules. Je disais plus haut que les types paramétriques sont des foncteurs, voyons cela avec le type générique des listes.

    (* on plonge les types dans le monde des modules *)
    module type T = sig type t end
    (* le type des listes est un foncteur *)
    module ListF (A : T) = struct type t = A.t list end;;
    module ListF : functor (A : T) -> sig type t = A.t list end
    (* illustration sur les listes d'entiers *)
    module Int_list = ListF (struct type t = int end);;
    module Int_list : sig type t = int list end
    (* pas de problèmes de typage *)
    ([1; 2; 3] : int_list);;
    - : int_list = [1; 2; 3]
    ([1; 2; 3] : Int_list.t);;
    - : Int_list.t = [1; 2; 3]

    Maintenant si on veut que notre module paramétré (notre foncteur) n'opère par sur n'importe quel type mais sur des types munis de certaines opérations, on le précise par une contrainte de types sur la signature du paramètres.

    (* les contraintes de type sur le paramètre *)
    module type S = sig
     type t
     val of_string : string -> t
     val meth : t -> int
    end
    (* notre module paramétré *)
    module A (M : S) = struct
     type owner = M.t
     let f () = M.(meth (of_string "foo"))
    end

    Voilà : A ne connaît absolument rien des modules qui lui seront passés en paramètre si ce n'est qu'ils doivent satisfaire les contraintes de la signature S.

    On peut définir ensuite plus loin, dans d'autres entités, des modules à lui passer.

    module M : S = struct
     type t = string
     let of_string x = x
     let meth = String.length
    end
    module N : S = struct
     type t = int
     let of_string s = try int_of_string s with _ -> 0
     let meth i = i
    end
    module B = A (M)
    module C = A (N);;
    (* note la valeur des types [owner] dans chacune des applications *)
    module B : sig type owner = M.t val f : unit -> int end
    module C : sig type owner = N.t val f : unit -> int end
    (* application de [f] pour les modules résultants *)
    B.f ();;
    - : int = 3
    C.f ();;
    - : int = 0

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.