• Aucun résultat trouvé

3.2 Exceptions

4.1.1 Variants

C H A P I T R E Q U A T R E

D´ efinitions de types et de modules

4.1 D´ efinition de types de donn´ ees

En Flow Caml, le programmeur peut d´efinir de nouvelles structures de donn´ees grˆace aux types de donn´ees alg´ebriques, variants et enregistrements. Comme en Objective Caml, ces types sont d´efinis grˆace aux d´eclarations introduites par le mot-clef . La forme de celles-ci est tr`es voisine de celle d’Objective Caml. Elles comportent toutefois quelques annotations suppl´ementaires, requises par l’analyse de flots d’information.

4.1.1 Variants

Je commence cette pr´esentation des types de donn´ees avec quelques exemples de types variants.

Voici un type `a quatre variantes permettant de repr´esenter les points cardinaux :

’a cardinal = North

| West

| South

| East

# ’a

;;

type (#’a:level) cardinal = North | West | South | East # ’a

Dans le syst`eme de type de Flow Caml, l’information port´ee par une valeur de type cardinalest d´ecrite par un niveau — exactement comme pour les types ´enum´er´es pr´ed´efinis tels que les entiers ou les caract`eres. Ce niveau est pr´ecis´e par la clause ’aqui termine la d´efinition du type somme : il s’agit ici de l’unique param`etre du constructeur de typecardinal.

Lorsqu’un type est d´efini, la boucle interactive reproduit sa d´eclaration en pr´ecisant explici-tement la signature du constructeur de type (cette derni`ere est automatiquement inf´er´e par le syst`eme `a partir de la d´eclaration entr´ee par le programmeur). Cette signature donne la sorte et la variance de chaque param`etre : dans l’exemple pr´ec´edent, ’a: signifie que ’a est un param`etre de sorte , covariant et gard´e.

p0 = North;;

val p0 : ’a cardinal

p1 : !alice cardinal = North;;

66 Chapitre 4·D´efinitions de types et de modules

val p1 : !alice cardinal

p2 = y2 North South;;

val p2 : !bob cardinal

Je d´efinis maintenant une fonctionrotatequi prend pour argument un point cardinal et retourne son successeur dans l’ordre des aiguilles d’une montre :

rotate =

Comme refl´et´e par le sch´ema de type inf´er´e, cette fonction prend un point cardinal de niveau quelconque, et retourne un autre point cardinal du mˆeme niveau.

Dans les sections2.1.3et2.1.4, nous avons rencontr´e deux exemples de types variants, qui sont pr´ed´efinis dans le langage Flow Caml : les listes et les options. Bien entendu, ces types n’ont rien de particulier (except´e le lieu de leur d´efinition), et peuvent ˆetre d´efinis comme suit :

La d´efinition liste toutes les formes possibles d’une valeur de type (’a, ’b) option : il s’agit soit de la constante Nonesoit du constructeur Someavec un argument de type ’b. La quatri`eme ligne de la d´eclaration, ’b, indique que le param`etre’best le niveau d’information associ´e `a la connaissance de la forme de l’option,i.e. NoneouSome.

La r´eponse produite par la boucle interactive lorsque cette d´eclaration est entr´ee donne `a nouveau la signature du constructeur de type d´efini :+’a: signifie que le premier param`etre de optionest covariant et de sorte type ; tandis que ’b: signifie que le second param`etre est un niveau, covariant et gard´e.

La d´eclaration du typelistest naturellement r´ecursive, mais cela n’a pas d’incidence particu-li`ere. Elle est ainsi similaire `a la pr´ec´edente :

En suivant ce mod`ele, on peut ´egalement d´efinir un type de donn´ees pour construire des arbres binaires ´etiquet´es :

puis ´ecrire une fonction qui calcule la hauteur d’un arbre :

4.1·D´efinition de types de donn´ees 67

Comme pour la fonction qui calcule la longueur d’une liste, le sch´ema de type inf´er´e indique que la hauteur d’un arbre d´epend de sa structure (i.e. l’imbrication des constructeurs Leafet None), mais pas des valeurs stock´ees dans les nœuds.

Munir une d´efinition de type extraite d’un programme Objective Caml des annotations n´eces-saires pour le langage Flow Caml n´ecessite g´en´eralement de choisir entre plusieurs alternatives, en fonction des propri´et´es de s´ecurit´e que l’on souhaite v´erifier. Par exemple, consid´erons la d´efinition en Objective Caml d’un type pour repr´esenter des arbres binaires ´etiquet´es par des entiers :

int_tree = ILeaf

| INode int_tree * int * int_tree

;;

Il y a au moins deux mani`eres pertinentes de« d´ecorer»cette d´eclaration pour la transposer en Flow Caml. Une premi`ere solution consiste `a sp´ecialiser la d´efinition du typetreedonn´ee ci-avant au cas des entiers :

Le type(’a, ’b) int_treeest isomorphe `a (’a int, ’b) tree. Ainsi, le type d’un arbre est annot´e par deux niveaux, ’aet ’b: le premier d´ecrit l’information attach´ee aux entiers stock´es dans l’arbre, tandis que le second est reli´e `a la structure de l’arbre. Pour illustrer l’int´erˆet de cette distinction, d´efinissons deux fonctions : size, qui calcule le nombre de nœuds d’un arbre, et sum, qui effectue la somme de ses ´etiquettes :

size =

Ces deux fonctions re¸coivent des types diff´erents, puisque la premi`ere ne r´ev`ele de l’information que sur la structure de l’arbre qui lui est pass´e en argument, alors que la seconde utilise ´egalement la valeur des ´etiquettes.

Une alternative possible consiste `a annoter le type des arbres binaires d’´etiquettes enti`eres avec un seul niveau d’information :

’a int_tree1 = ILeaf1

| INode1 ’a int_tree1 * ’a int * ’a int_tree1

68 Chapitre 4·D´efinitions de types et de modules

# ’a

;;

type (#’a:level) int_tree1 = ILeaf1

| INode1 of ’a int_tree1 * ’a int * ’a int_tree1

# ’a

;;

L’information port´ee par les ´etiquettes n’est plus ici distingu´ee de celle associ´ee `a la structure des arbres. Ce choix permet d’obtenir des types plus simples et plus concis, mais ´egalement moins pr´ecis. Par exemple, les deux fonctions calculant la taille et la somme des ´etiquettes d’un arbre re¸coivent, avec cette d´eclaration, le mˆeme sch´ema de type :

size1 =

ILeaf1 -> 0

| INode1 (tl, x, tr) -> size1 tl + 1 + size1 tr

;;

val size1 : ’a int_tree1 -> ’a int

sum1 =

ILeaf1 -> 0

| INode1 (tl, x, tr) -> sum1 tl + x + sum1 tr

;;

val sum1 : ’a int_tree1 -> ’a int

Le type obtenu pour size1 donne ainsi une description moins pr´ecise du comportement de la fonction que celui desize: il ne refl`ete pas l’absence de d´ependance entre les ´etiquettes de l’arbre et sa taille, comme montr´e par les expressions suivantes :

size (INode (ILeaf, x1, ILeaf));;

- : ’a int

size1 (INode1 (ILeaf1, x1, ILeaf1));;

- : !alice int