• [^] # Re: go 2.0

    Posté par . En réponse au journal Pourquoi la recherche en langages de programmation ?. Évalué à 3.

    Mais je trouve que leur forme dans ocaml est très complexe et très peu lisible.

    J'ai fini par m'y habituer, mais il est vrai que le langage des modules et foncteurs est assez verbeux. Après sur la syntaxe concrète de définition d'un module sans paramètre, je ne vois pas bien la différence entre l'usage d'accolades ou celle de mots-clés :

    module type M = sig
     type t
     val v : t
    end
    (* VS *)
    module type M = {
     type t ;
     v : t ;
    }

    Par exemple, en Coq, où la distinction entre type et valeur n'a pas lieu d'être (les types sont des valeurs comme les autres et vivent dans le même monde), on utilisera la syntaxe des enregistrements pour écrire cela (même si Coq a aussi son système de modules).

    Record M : Type := mkM {
     t : Type ;
     v : t ;
    }.

    Quoi qu'il en soit, je ne trouve pas la syntaxe concrète pour écrire des modules ou leur signature si illisible que cela. Même dans les usages simples des foncteurs (qui sont des fonctions des modules vers les modules), je trouve la notation assez proche du langage des fonctions du langage.

    (*
     une signature pour exprimer la possibilité de
     convertir un type quelconque vers un string :
     c'est la donné conjointe d'un type et d'une
     fonction de conversion.
    *)
    module type Showable = sig
     type t
     val show : t -> string
    end
    (*
     à partir d'un tel module, il n'est pas difficile
     de définir des fonctions pour afficher le type
     à l'écran. On passera par un foncteur.
     On commence par étendre la signature précédente
     pour ajouter des fonctions d'affichage, puis celle
     du foncteur qui transforme un module du premier genre
     dans le second
    *)
    module type Printable = sig
     include Showable
     val print : t -> unit
     val println : t -> unit
    end
    module type Print_abs = functor (M : Showable) -> Printable
    module type Print = functor (M : Showable) -> Printable with type t = M.t

    Ici, c'est là que le langage commence à devenir verbeux, mais c'est par nécessité. Un module permet de cacher au monde extérieur la représentation concrète de ses types : pour faire en sorte que les données du type de M : Showable soient compatibles avec celles du module résultant de l'application Print(M), il faut le préciser explicitement via l'annotation with type t = M.t. Sans cela, le vérificateur de type se plaindra :

    module Show_int = struct
     type t = int
     let show = string_of_int
    end
    module Print_abs : Print_abs = functor (M : Showable) -> struct
     type t = M.t
     let show = M.show
     let print x = print_string (show x)
     let println x = print_endline (show x)
    end
    module Print : Print = functor (M : Showable) -> struct
     type t = M.t
     let show = M.show
     let print x = print_string (show x)
     let println x = print_endline (show x)
    end;;
    module Print_int_abs = Print_abs (Show_int)
    module Print_int = Print (Show_int)
    (* incompatibilité des types *)
    Print_int_abs.println 1;;
    Error: This expression has type int but an expression was expected of type
    Print_int_abs.t
    (* types compatibles *)
    Print_int.println 1;;
    1
    - : unit = ()

    Cependant l'aspect verbeux peut, dans les faits, se limiter aux fichiers d'interface .mli et le code effectif du fichier .ml être tout simplement :

    module Show_int = struct
     type t = int
     let show = string_of_int
    end
    module Print (M : Showable) = struct
     type t = M.t
     let show = M.show
     let print x = print_string (show x)
     let println x = print_endline (show x)
    end
    module Print_int = Print (Show_int)

    Cela parait peut être évident pour toi qui est tombé dedans que tu es petit.

    Je ne suis pas tombé dedans quand j'étais petit : c'est un niveau d'abstraction (auquel je suis largement habitué pour d'autres raisons, et encore je ne trouve pas cela très abstrait) dont j'ai ressenti le besoin, et j'ai appris la façon dont OCaml le met à disposition et à m'en servir. Si on n'en ressent pas soi même le besoin pour des raisons de généralisation, le concept de module semble tombé comme un cheveux sur la soupe.

    Les modules ressemblent ici vaguement à des objets, avec des définitions de type dedans, mais avec une autre syntaxe.

    Les deux syntaxes sont assez proches : ce sont les mots-clés qui changent. Cela me semble utile, voire conseiller au niveau du principe de moindre surprise, le second généralisant (en quelque sorte) le premier il serait surprenant que la syntaxe ne le signifie pas par un moyen quelconque : autrement on risquerait de confondre les deux notions.

    Pour conclure ce commentaire déjà bien long, comme toutes ces questions tournent autour de la notion d'abstraction (fonction, type paramétré, objet, module, foncteur...) il est normal que le lamba-calcul soit un outil théorique de premier choix. Dans ce langage, il n'y a que deux notions fondamentales : l'abstraction (le lambda) et l'usage d'abstraction (l'application). :-)

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