Author: Christian.Queinnec@lip6.fr

## Extensions du langage

O'Labl apporte trois extensions à Objective CAML. Deux d'entre elles ne modifient pas le reste du langage dans le sens où un programme écrit dans la version 2.04 conserve le même comportement dans la version 2.99. La troisième extension et les modifications qui ont été apportées aux bibliothèques standards sont susceptibles de changer le sens des programmes écrits sans tenir compte des nouveaux traits du langage. Pour assurer une compatibilité avec le passé, il existe deux modes distincts pour utiliser Objective CAML 2.99 :

1. Le mode classique : c'est l'utilisation par défaut du compilateur et de la boucle d'interaction. L'extension constituée par les labels est restreinte pour ne pas modifier la sémantique des programmes qui ne les utilisent pas.
2. Le mode moderne : il est choisi explicitement. L'extension des labels permet plus de choses, mais l'utilisation des bibliothèques demande d'utiliser les spécificités de ce nouveau trait.

### Moderne ou Classique

Le compilateur et la boucle d'interaction possèdent une nouvelle option (`-modern`) pour explicitement utiliser les nouveaux traits des labels. Par défaut, le style classique restreint les possibilités des labels et conserve la compatibilité avec les compilateurs précédents.

    $ ocamlc -modern .....
    $ ocaml -modern .....

La boucle d'interaction autorise de commuter les deux modes pendant son utilisation en utilisant la directive #modern.

    # #modern true ;;   (* passe en mode moderne *)
    # #modern false ;;  (* passe en mode classique *)

### Warning

Un programme correct en 2.04, même s'il n'utilise aucune des extensions de la 2.99, peut ne plus être correct dans la nouvelle version.

### Labels

Un est une annotation apportée aux arguments d'une fonction dans sa déclaration et dans son application. Il se présente comme un identificateur séparé du paramètre (formel ou effectif) d'une fonction par le symbole `'`:`'`.
On trouve les labels dans les déclarations de fonctions :

### Syntaxe

let fonction label:p = exp

dans les déclarations anonymes avec le mot clé fun :

### Syntaxe

fun label:p -> exp

et dans le paramètre effectif d'une fonction :

### Syntaxe

( fonction label:exp )

##### labels dans les types

Les labels donnés aux arguments d'une expression fonctionnelle apparaissent dans son type et annotent les types des arguments auxquels ils réfèrent.

    # let add op1:x op2:y = x + y ;;
    val add : op1:int -> op2:int -> int = <fun>

    # let mk_triplet arg1:x arg2:y arg3:z = (x,y,z) ;;
    val mk_triplet : arg1:'a -> arg2:'b -> arg3:'c -> 'a * 'b * 'c = <fun>

Si on souhaite donner le même identificateur au label et à la variable, il n'est pas utile de le répéter car c'est celui pris par défaut.

### Syntaxe

fun :p -> exp

    # let mk_triplet :arg1 :arg2 :arg3 = (arg1,arg2,arg3) ;;
    val mk_triplet : arg1:'a -> arg2:'b -> arg3:'c -> 'a * 'b * 'c = <fun>

L'utilisation du caractère : dans les déclarations de labels impose d'utiliser des espaces pour les autres utilisations de ce symbole à des endroits où il pourrait y avoir ambiguïté.

    # let id x:int = int ;;   (* x est le label du paramètre int *)
    val id : x:'a -> 'a = <fun>
    # let id x : int = x ;;   (* x est un paramètre de type int  *)
    val id : int -> int = <fun>

### Warning

La contrainte d'un type dans une expression doit se faire en respectant la syntaxe des déclarations de labels.

Il n'est pas autorisé de définir des labels dans une déclaration de fonction par filtrage; en conséquence le mot clé function ne peut pas être utilisé pour une fonction avec label.

    # let f = function arg:x -> x ;;
    Characters 18-22:
    Syntax error
    # let f = fun arg:x -> x ;;
    val f : arg:'a -> 'a = <fun>

#### labels en mode classique

Dans un premier temps nous présentons les labels dans le mode classique.

##### Application

Les labels peuvent être utilisés pour appliquer les arguments à une fonction, mais ils ne sont pas obligatoires.

    # mk_triplet '1' 2 3.0 ;;
    - : char * int * float = '1', 2, 3
    # mk_triplet arg1:'1' arg2:2 arg3:3.0 ;;
    - : char * int * float = '1', 2, 3
    # mk_triplet '1' arg2:2 3.0 ;;
    - : char * int * float = '1', 2, 3

Les labels ne modifient pas les possibilités d'application partielle.

    # let couple = mk_triplet arg1:() ;;
    val couple : arg2:'_a -> arg3:'_b -> unit * '_a * '_b = <fun>

### Warning

Dans le mode classique, les arguments d'une fonction doivent être appliqués dans l'ordre de leur déclaration.

    # let couple = mk_triplet arg2:() ;;
    Characters 30-32:
    Expecting function has type arg1:'a -> arg2:'b -> arg3:'c -> 'a * 'b * 'c
    This argument cannot be applied with label arg2:

##### Lisibilité du code

L'intérêt principal des labels est d'associer au type d'un argument une annotation qui rend plus explicite l'interface d'une fonction.

    # String.sub ;;
    - : string -> pos:int -> len:int -> string = <fun>

Plus aucun doute, la fonction `String.sub` prend en argument une chaîne, la position du premier caractère, et la longueur de la chaîne à extraire.
Les fonctions des bibliothèques de la distribution d'Objective CAML 2.99 ont été << labelisées >>. Le tableau B.1 donne les conventions qui ont été utilisées.

> label | signification
> pos: | une position (dans une liste, une chaîne, un tableau, etc.)
> len: | une taille (length)
> buf: | une chaîne utilisée comme tampon (buffer)
> src: | la source d'une opération
> dst: | la destination d'une opération
> fun: | une fonction à appliquer
> pred: | un prédicat
> acc: | un accumulateur
> to: | un canal de sortie (out_channel)
> key: | une clé utilisée dans un index (liste d'associations, etc.)
> data: | une valeur associée utilisée dans un index
> mode: | un mode ou une liste d'options
> perm: | des permissions de fichiers

> Figure B.1 : conventions sur les labels

#### Labels en mode moderne

La principale différence avec le mode classique est que dans le mode moderne, il est obligatoire de donner explicitement les labels lors de l'application d'une expression fonctionnelle.

    # let add :x :y = x + y ;;
    val add : x:int -> y:int -> int = <fun>
    # #modern false ;;
    # add 1 2 ;;
    - : int = 3
    # #modern true ;;
    # add 1 2 ;;
    Characters 4-5:
    Expecting function has type x:int -> y:int -> int
    This argument cannot be applied without label

### Warning

Dans le mode moderne, il est obligatoire de donner le nom des labels aux arguments qui en possèdent un.

La conséquence de cette obligation est que l'ordre des arguments possédant un label devient pour le coup indifférent puisqu'on peut les identifier par leur label. Objective CAML 2.99 possède un système permettant de commuter les arguments d'une fonction et donc de les appliquer dans n'importe quel ordre.

    # add x:1 y:2 ;;
    - : int = 3
    # add y:2 x:1 ;;
    - : int = 3

C'est particulièrement utile quand on souhaite faire l'application partielle d'un argument qui n'est pas le premier de la déclaration.

    # let add_x_2 = add y:2  ;;
    val add_x_2 : x:int -> int = <fun>
    # add_x_2 x:1 ;;
    - : int = 3

Les arguments possédant le même label ou n'ayant pas de label ne commutent pas entre eux. Une application dans un tel cas considère le premier argument qui a ce label (respectivement qui n'a pas de label).

    # let test arg1:_ arg2:_  _  arg2:_  _ = () ;;
    val test : arg1:'a -> arg2:'b -> 'c -> arg2:'d -> 'e -> unit = <fun>

    # test arg2:() ;;  (* le premier label arg2: de la déclaration *)
    - : arg1:'a -> 'b -> arg2:'c -> 'd -> unit = <fun>

    # test () ;;       (* le premier argument sans label  de la déclaration *)
    - : arg1:'a -> arg2:'b -> arg2:'c -> 'd -> unit = <fun>

### Paramètres optionnels

Objective CAML 2.99 autorise la définition de fonctions avec des arguments labelisés optionnels. De tels arguments sont définis avec une valeur par défaut qui est la valeur donnée au paramètre si l'application n'en donne pas une autre explicitement.

### Syntaxe

fun ?(nom: p = exp1) -> exp2

Le caractère optionnel de l'argument apparaît dans son type par le symbole ?.

    # let sp_incr ?(inc:x=1) y =  y := !y + x ;;
    val sp_incr : ?inc:int -> int ref -> unit = <fun>

La fonction `sp_incr` se comporte comme la fonction `incr` du module `Pervasives`.

    # let v = ref 4  in  sp_incr v ; v ;;
    - : int ref = {contents=5}

Mais on peut lui spécifier un incrément différent de celui par défaut.

    # let v = ref 4  in  sp_incr inc:3 v ; v ;;
    - : int ref = {contents=7}

L'application d'une fonction se fait en donnant la valeur par défaut de tous les paramètres optionnels jusqu'au paramètre effectivement passé par l'application. Si l'argument de l'appel est donné sans label, il est considéré comme étant le premier argument de la fonction qui ne soit pas optionnel.

    # let f ?(:x1=0) ?(:x2=0) x3 x4 = 1000*x1+100*x2+10*x3+x4 ;;
    val f : ?x1:int -> ?x2:int -> int -> int -> int = <fun>
    # f 3 ;;
    - : int -> int = <fun>
    # f 3 4 ;;
    - : int = 34
    # f x1:1 3 4 ;;
    - : int = 1034
    # f x2:2 3 4 ;;
    - : int = 234

Les arguments optionnels peuvent être passés dans un ordre arbitraire quel que soit le mode en cours.

    # #modern false ;;
    # f x2:2 x1:1 3 4 ;;
    - : int = 1234

Un argument optionnel peut être donné sans valeur par défaut, dans ce cas il est considéré dans le corps de la fonction comme étant du type 'a option; `None` est sa valeur par défaut.

### Syntaxe

fun ?nom: p -> exp

    # let print_entier ?file:x n =
       match x with
           None -> print_int n
         | Some f -> let fic = open_out f in
                     output_string fic (string_of_int n) ;
                     output_string fic "\n" ;
                     close_out fic ;;
    val print_entier : ?file:string -> int -> unit = <fun>

Par défaut, la fonction `print_entier` affiche son argument sur la sortie standard. Si on lui passe un nom de fichier avec le label `file`, la sortie se fait alors dans ce fichier.

### Remarque

Si les derniers paramètres d'une fonction sont optionnels, il devront être appliqués explicitement.

    # let test ?:x ?:y  n  ?:a ?:b  =  n ;;
    val test : ?x:'a -> ?y:'b -> 'c -> ?a:'d -> ?b:'e -> 'c = <fun>
    # test 1 ;;
    - : ?a:'_a -> ?b:'_b -> int = <fun>
    # test 1 b:'x' ;;
    - : ?a:'_a -> int = <fun>
    # test 1 a:() b:'x' ;;
    - : int = 1

### Labels et objets

Les labels peuvent être utilisés pour les paramètres d'une méthode ou pour ceux du constructeur d'un objet.

    # class point ?(:x=0) ?(:y=0) (col : Graphics.color) =
       object
         val pos = (x,y)
         val couleur = col
         method affiche ?(to:file=stdout) () =
             output_string file "point (" ;
             output_string file (string_of_int (fst pos)) ;
             output_string file "," ;
             output_string file (string_of_int (snd pos)) ;
             output_string file ")\n"
       end ;;
    class point :
      ?x:int ->
      ?y:int ->
      Graphics.color ->
      object
        val couleur : Graphics.color
        val pos : int * int
        method affiche : ?to:out_channel -> unit -> unit
      end

    # let obj1 = new point x:1 y:2 Graphics.white
     in obj1#affiche () ;;
    point (1,2)
    - : unit = ()
    # let obj2 = new point Graphics.black
     in obj2#affiche () ;;
    point (0,0)
    - : unit = ()

Les labels et les arguments optionnels fournissent une alternative à la surcharge des méthodes et des constructeurs qui sont classiques dans les langages à objets et qu'Objective CAML ne peut accepter.
Cette émulation de la surcharge comporte quelques limitations, en particulier un des arguments devra ne pas être optionnel; on peut toujours utiliser un argument de type unit.

    # class nombre ?:entier ?:flottant () =
       object
         val mutable valeur = 0.0
         method print = print_float valeur
         initializer
           match (entier,flottant) with
               (None,None) | (Some _,Some _) -> failwith "construction incorrecte"
             | (None,Some f) -> valeur <- f
             | (Some n,None) -> valeur <- float_of_int n
       end ;;
    class nombre :
      ?entier:int ->
      ?flottant:float ->
      unit -> object val mutable valeur : float method print : unit end

    # let n1 = new nombre entier:1 () ;;
    val n1 : nombre = <obj>
    # let n2 = new nombre flottant:1.0 () ;;
    val n2 : nombre = <obj>

### Constructeurs polymorphes

Les types sommes d'Objective CAML ont deux principales limitations. D'une part il n'est pas possible d'étendre un type somme avec un nouveau constructeur. D'autre part un constructeur ne peut appartenir qu'à un seul type. Objective CAML 2.99 propose des constructeurs spéciaux dit constructeurs polymorphes qui échappent à ces deux contraintes.
Un constructeur polymorphe est bâti avec un identificateur qui a la même contrainte syntaxique que les autres constructeurs à savoir qu'il doit être préfixé par une majuscule.

### Syntaxe

`Nom

ou

### Syntaxe

`Nom type

Un ensemble de constructeurs polymorphes forme un type, mais il n'est pas nécessaire de définir un type pour définir un constructeur polymorphe.

    # let x =  `Entier 3 ;;
    val x : [>`Entier int] = `Entier 3

Le type de `x` avec le symbole [> se lit comme le type qui contient au moins le constructeur ``Entier` int.

    # let int_of = function
         `Entier n -> n
       | `Reel r -> int_of_float r ;;
    val int_of : [<`Entier int|`Reel float] -> int = <fun>

A l'inverse, le symbole [< indique que l'argument de `int_of` est du type qui contient au plus les constructeurs ``Entier` int et ``Reel` float.
Il est tout de même possible de définir un type par énumération de ses constructeurs polymorphes :

### Syntaxe

type t = [ `Nom1 | `Nom2 | ... | `Nomn ]

ou pour des types paramétrés :

### Syntaxe

type ('a,'b,...) t = [ `Nom1 | `Nom2 | ... | `Nomn ]

    # type valeur = [ `Entier int | `Reel float ] ;;
    type valeur = [`Entier int|`Reel float]

Les constructeurs polymorphes, comme leur nom l'indique, peuvent prendre des arguments de plusieurs valeurs.

    # let v1 = `Nombre 2
     and v2 = `Nombre 2.0 ;;
    val v1 : [>`Nombre int] = `Nombre 2
    val v2 : [>`Nombre float] = `Nombre 2

Mais pour autant, `v1` et `v2` ne sont pas du même type.

    # v1=v2 ;;
    Characters 4-6:
    This expression has type [>`Nombre float] but is here used with type
      [>`Nombre int]

D'une manière plus générale, les contraintes sur le type des arguments des constructeurs polymorphes sont accumulées dans leur type par l'annotation &.

    # let test_nul_entier = function `Nombre n -> n=0
     and test_nul_reel = function `Nombre r -> r=0.0 ;;
    val test_nul_entier : [<`Nombre int] -> bool = <fun>
    val test_nul_reel : [<`Nombre float] -> bool = <fun>
    # let test_nul x = (test_nul_entier x) || (test_nul_reel x) ;;
    val test_nul : [<`Nombre int & float] -> bool = <fun>

Le type de `test_nul` indique que les seules valeurs acceptées par cette fonction sont celles avec le constructeur ``Nombre` et un argument qui est à la fois de type int et de float. C'est à dire que les seules valeurs acceptables sont de type 'a !

    # let f () = test_nul (failwith "rend une valeur de type 'a") ;;
    val f : unit -> bool = <fun>

Les types des constructeurs polymorphes sont eux-mêmes susceptibles d'être polymorphes.

    # let id = function `Ctor -> `Ctor ;;
    val id : [<`Ctor] -> [>`Ctor] = <fun>

Le type de la valeur de retour de `id` est << les ensembles de constructeurs qui contiennent au moins ``Ctor` >> donc c'est un type polymorphe qui peut s'instancier en un type plus précis. De même, l'argument de `id` est << les ensembles de constructeurs qui contiennent au plus ``Ctor` >> qui lui aussi est susceptible d'être précisé. En conséquence, ils suivent le mécanisme général des types polymorphes d'Objective CAML à savoir qu'ils sont susceptibles d'être affaiblis.

    # let v = id `Ctor ;;
    val v : _[>`Ctor] = `Ctor

`v` étant le résultat d'une application, son type n'est pas polymorphe (d'où la présence du caractère `_`).

    # id v ;;
    - : _[>`Ctor] = `Ctor

`v` est monomorphe et son type est un sous-type de << contient au moins le constructeur ``Ctor` >>. Le fait de l'appliquer à `id` va forcer son type à être un sous-type de << contient au plus le constructeur ``Ctor` >>. Logiquement, il doit maintenant avoir le type << contient exactement ``Ctor` >>. Ce que nous vérifions.

    # v ;;
    - : [`Ctor] = `Ctor

À la manière des classes, les types des constructeurs polymorphes peuvent être ouverts.

    # let is_entier = function
        `Entier (n : int) -> true
       | _ -> false ;;
    val is_entier : [<`Entier int| ..] -> bool = <fun>
    # is_entier (`Entier 3) ;;
    - : bool = true
    # is_entier `Autre ;;
    - : bool = false

Tous les constructeurs sont acceptés, mais le constructeur ``Entier` doit avoir un argument entier.

    # is_entier (`Entier 3.0) ;;
    Characters 12-23:
    This expression has type [>`Entier float] but is here used with type
      [<`Entier int| ..]

Toujours comme les classes, les types des constructeurs peuvent être cycliques.

    # let rec long = function `Rec x -> 1 + (long x) ;;
    val long : ([<`Rec 'a] as 'a) -> int = <fun>

Pour finir, notons que le type peut être en même temps un sous-ensemble et un sur ensemble de constructeurs. Commençons par un exemple simple.

    # let ex1 = function `C1 -> `C2 ;;
    val ex1 : [<`C1] -> [>`C2] = <fun>

Maintenant nous identifions les types d'entrée et de sortie de l'exemple par un second filtre.

    # let ex2 = function `C1 -> `C2 | x -> x ;;
    val ex2 : ([<`C2|`C1| .. >`C2] as 'a) -> 'a = <fun>

Nous obtenons donc le type ouvert qui contient au moins ``C2` puisque le type de retour contient au moins ``C2`.

    # ex2 ( `C1 : [> `C1 ] ) ;;  (* est bien un sous type de [<`C2|`C1| .. >`C2] *)
    - : _[>`C2|`C1] = `C2
    # ex2 ( `C1 : [ `C1 ] ) ;;   (* n'est pas un sous type de [<`C2|`C1| .. >`C2] *)
    Characters 6-9:
    This expression has type [`C1] but is here used with type [<`C2|`C1| .. >`C2]