• [^] # Re: Type fantôme

    Posté par . En réponse au journal Une petite histoire d'utilisation type fort dans Ocaml. Évalué à 1.

    L'intérêt que je vois au type fantôme, c'est qu'ils permettent de rafiner un type de base, en rajoutant potentiellement de l'information connue à la compilation, et ensuite de définir des jeux de fonctions qui utilisent −optionnellement− cette information supplémentaire, le tout en gardant explicite qu'il s'agit du même type de base.

    Par exemple, on pourrait imaginer un type liste augmenté d'une information sur le fait qu'elle soit triée ou non

    type triee = private Sorted
    type non_triee = private Unsorted
    type ('a,'sorted_or_not) liste = 'a list

    À partir de là, je peux raffiner les fonctions sur les listes pour rajouter de l'information:

     val sort: ('a,'triee_ou_pas) liste -> ('a,triee) liste
    (** triée une liste la rend triée *)
     val rev: ('a,'tree_ou_pas) liste -> ('a, non_triee) liste
    (** renversée une liste triée ou non, donne une liste non triée *)

    Cela permet aussi de définir des fonctions qui requiert en entrée des listes triées, ce que le type fantôme permet de garantir

    val affiche_triee:(int,triee) liste -> unit

    Mais la grande différence par rapport à avoir deux types abstraits, c'est qu'il est beaucoup plus simple de partager les fonctions entre les différents sous-types dans les cas où ces fonctions n'ont pas besoin de l'information supplémentaire:

    val iter: ('a -> unit) -> ('a, 'triee_ou_pas ) liste -> unit

    Un autre point sur lesquels des types fantômes peuvent être intéressant, ce sont les bindings avec des bibliothèques extérieures en C. Les types fantômes peuvent permettre d'introduire un typage correct sans trop s'éloigner de la représentation en C. Par exemple, j'avais utilités dans un prototype de bindings OpenGL des types fantômes pour typer les enums et handles opengl, qui sont tous des entiers dans la représentation C. Ce qui permet de transformer

    val bindBuffer : int -> int -> unit

    en

    type 'a gl_enum = private int
    type 'a handle = private int
    val bindBuffer : [>`BufferType] gl_enum -> [`Buffer] handle -> unit

    et de s'assurer que système de type d'OCaml vérifiera à notre place que seulement des identifiants valides seront passés à l'API OpenGL; tout en pouvant facilement repasser à des entiers non typés au besoin.