• Aucun résultat trouvé

´etait aussi v´erifi´ee. Toutes ces pr´ecieuses informations de typage peuvent ˆetre recouvr´ees dans le corps de la branche associ´ee `a ce motif. Ainsi, de l’´evaluation de a r´esulte une valeur de type β1, de celle de b une valeur de type β2. Le couple (eval(a), eval(b)) est donc du type β1× β2, c’est-`a-dire un type ´egal

`a α ce qui rend la branche bien typ´ee. Dans le cas de Pair , notons que des variables universellement quantifi´ees (β1 et β2) sont introduites automatiquement par le typeur.

Exhaustivit´e plus pr´ecise

Pour finir, la pr´ecision accrue des sch´emas de type des constructeurs de donn´ees induit un meilleur diagnostic statique de l’exhaustivit´e des pattern matchings. En effet, reprenons l’exemple de la fonc- tion eval mais sp´ecialisons son type en term int → int. On restreint le domaine d’application de l’´evaluateur en n’´evaluant que des termes de type int. Le typeur peut donc d´eterminer statiquement que la branche IsZ peut ˆetre supprim´ee sans atteinte `a l’exhaustivit´e de l’analyse car les valeurs construites `a partir de IsZ sont de type term bool.

3.2 ´Egalit´e entre types

Le traitement des ´egalit´es de type associ´ees aux constructeurs de donn´ees est au centre des pro- bl`emes de v´erification et d’inf´erence de type lorsque l’on ajoute les GADT `a ML. Dans cette section, nous tˆachons de donner informellement une id´ee de ces difficult´es.

3.2.1 R`egle de conversion

Si on tente de formaliser les arguments qui nous ont convaincus du bon typage de la fonction eval de la section3.1.3, plusieurs ingr´edients interviennent.

En premier lieu, la pr´esence d’´egalit´es de type locales aux corps des branches induit l’existence d’un ensemble d’´egalit´es de type valides en chaque point du programme. Du point de vue de la formalisation, cela n´ecessite l’introduction d’un nouvel objet au sein des hypoth`eses du jugement de typage : la liste

E des ´egalit´es de types r´ecolt´ees dans le contexte o`u est plong´e le terme dont on veut d´eterminer le type. En cons´equence, la forme du jugement de typage pour ML muni de GADT est

Γ, E ` t : τ

et se lit dans l’environnement de typage Γ et sous les hypoth`eses d’´egalit´es de type E, le terme t a le type τ .

Pour saisir le rˆole de E, il faut alors se poser les deux questions suivantes : Quand augmente-t-on le syst`eme d’´equations E ? Quand utilise-t-on les ´egalit´es du syst`eme E ?

L’augmentation de E est effectu´ee par le typage des match. En effet, la confrontation du type du terme analys´e (dans eval t qui a le type term α) et du type du motif (ici, le motif Lit(i) qui a le type

term int) implique une ´egalit´e entre types (par exemple, l’´egalit´e α = int). Le syst`eme d’´equations E

est donc localement augment´e dans la branche par cette ´egalit´e.

L’utilisation de E est moins localis´ee dans le sens o`u elle ne d´epend pas de la forme du terme qu’on cherche `a typer. En d’autres termes, aucune indication syntaxique ne permet de d´ecider o`u utiliser les ´equations de E. En fait, la contribution de E est implicite car elle contraint le typage `a s’effectuer modulo toutes les ´egalit´es de type impliqu´ees par E. Nous avons utilis´e cette propri´et´e dans notre exemple pour montrer que le corps de la branche avait bien le type attendu pour le retour de la fonction eval. Plus pr´ecis´ement, voici la d´erivation que nous avons utilis´e dans notre explication informelle :

eval : ∀α.term α → α, t : term α, i : int; α = int ` i : int α = int |= α = int eval : ∀α.term α → α, t : term α, i : int; α = int ` i : α

32 3. Extension de ML par des GADT

Elle se lit ainsi : sachant que le terme i est un entier et que le syst`eme d’´equations E (ici α = int) implique l’´egalit´e α = int alors le terme i est aussi de type α. Cette forme de r`egle de d´eduction est appel´ee r`egle de conversion car elle permet de convertir le type d’un terme en raisonnant non pas sur le terme mais sur son type. Voici la version g´en´erale de cette r`egle :

Γ, E ` t : τ2 Γ, E |= τ1= τ2

Γ, E ` t : τ1

Cette r`egle de conversion ainsi qu’une l´eg`ere modification de la r`egle habituelle pour typer le

pattern matching sont les seules modifications `a apporter `a ML pour introduire les types alg´ebriques

g´en´eralis´es. La d´ecidabilit´e du bon typage d´epend alors de la d´ecidabilit´e des implications entre ´egalit´es de type (comme le montre l’hypoth`ese de la forme Γ, E |= τ1 = τ2). Or, si on se limite aux types

standards de ML, ces implications sont d´ecidables efficacement par l’algorithme appel´e congruence

closure (Baader & Nipkow,1998,Nelson & Oppen,1980).

3.2.2 Probl`eme de l’inf´erence de types

Un des traits caract´eristiques du langage de programmation ML est l’inf´erence de types d´ecouverte par Roger Hindley et Robin Milner (Damas & Milner,1982). L’objectif de l’inf´erence de types est de calculer un type principal (le plus g´en´eral) pour tout programme ML bien typ´e. L’adaptation de cet algorithme est la difficult´e principale pos´ee par les types alg´ebriques g´en´eralis´es. Dans cette section, nous allons essayer de fournir une intuition sur la nature de cette difficult´e.

Perte de la principalit´e

La premi`ere difficult´e r´eside en la perte de la principalit´e. En effet, une fois les GADT introduits dans le langage, un programme ML ne poss`ede plus un type plus g´en´eral que tous les autres. Pour le comprendre, appuyons-nous sur l’exemple de la figure3.8.

type eq =

| Eq : ∀α.eq α α

let cast = λe.λx.match e with Eq → x

Fig. 3.8: Une fonction poss´edant plusieurs types principaux.

Ces deux sch´emas de type ML sont acceptables pour la fonction cast : – ∀αβγ. eq α β → γ → γ

– ∀αβ. eq α β → α → β – ∀αβ. eq α β → β → α

Si le premier sch´ema doit sembler naturel au lecteur intime avec l’inf´erence habituelle de ML, le second sch´ema n´ecessite une explication (le troisi`eme sch´ema est du mˆeme acabit). Une valeur de type eq peut ˆetre vue comme la preuve d’une ´egalit´e entre deux types. Ainsi, lorsque l’on apprend que le terme e, de type eq α β, est Eq, de type eq δ δ (pour un certain δ), on apprend du mˆeme coup l’´egalit´e

α = β = δ. Or, comme x est de type α, x est aussi de type β ce qui justifie le type de retour affect´e `a

la fonction cast.

Le point important, c’est qu’aucun de ces sch´emas de type n’est plus g´en´eral que l’autre (aucune instanciation des param`etres de l’un ne permet de retrouver l’autre et r´eciproquement). Nous verrons

3.2 ´Egalit´e entre types 33

dans la section suivante qu’il faut g´en´eraliser la syntaxe des types pour r´eussir `a trouver un type plus g´en´eral que ces trois l`a.

Cette perte de la principalit´e pose diverses probl`emes. Tout d’abord, la principalit´e est une pro- pri´et´e au centre de l’algorithme efficace d’inf´erence de types de Hindley-Milner car elle permet de traiter l’inf´erence de mani`ere incr´ementale sans retour en arri`ere. Ensuite, si un programme ne pos- s`ede pas un unique sch´ema de type plus g´en´eral, doit-on le rejeter, doit-on calculer l’ensemble des sch´emas de type plus g´en´eraux valides pour ce programme ou peut-on se permettre de faire un choix arbitraire entre tous les sch´emas possibles ?

D´etermination et utilisation des ´egalit´es de type

La seconde difficult´e est inh´erente `a la pr´esence de l’ensemble des ´egalit´es de type contenues dans le jugement de typage. Dans le cadre de l’inf´erence de types, les deux questions de la section pr´ec´edente, quelles sont les nouvelles ´egalit´es de type ?  et  o`u utilise-t-on ces ´egalit´es ?  ne sont plus ind´ependantes mais au contraire interd´ependantes.

Pour approfondir notre intuition sur ce probl`eme d’inf´erence de types, on peut rappeler les travaux de Fran¸cois Pottier et Vincent Simonet (Simonet & Pottier, 2007) sur HMG(X). Ils g´en´eralisent la r´eduction de l’inf´erence de types de ML `a la r´esolution de contraintes sous pr´efixes mixtes (Pottier & R´emy, 2005) en augmentant le langage des contraintes par des implications. Sans rentrer dans les d´etails, la contrainte `a r´esoudre dans le cas de cast est de la forme :

. . . def x : γ1 in γ2= γ3⇒ γ1= γ4. . .

Les variables γ1, γ2, γ3, γ4sont les variables de la contrainte de typage. Ici, la variable γ1correspond

au type de la variable x, les variables γ2et γ3sont telles que le terme e a le type eq γ2γ3et la variable γ4repr´esente le type de retour de la fonction cast. Pour r´esoudre cette contrainte, plusieurs op´erations

sont utiles.

Pour traiter cette implication, on doit naturellement explorer plusieurs voies de r´esolution. L’une suppose la partie gauche valide dans la partie droite c’est-`a-dire γ2 = γ3 et effectue la r´esolution

des ´egalit´es entre types modulo cette ´egalit´e. La seconde suppose la partie gauche invalide et relˆache alors la contrainte en ignorant la partie droite de l’implication. On touche du doigt une explosion combinatoire dans la r´esolution de ces contraintes et, effectivement, dans le cas g´en´eral, ce nouveau langage de contraintes n´ecessite un algorithme de r´esolution dont la complexit´e est non ´el´ementaire.

Un autre point important mis `a jour par l’´etude de HMG(X) (Simonet & Pottier, 2007) est la n´ecessit´e d’´etendre la syntaxe des sch´emas de type pour retrouver la propri´et´e de principalit´e du typage. Seuls des sch´emas de type contraints, i.e. de la forme ∀α[C].τ , permettent de capturer dans toute sa g´en´eralit´e le type d’un programme ML utilisant des GADT. Par exemple dans HMG(=) (Simonet & Pottier,2007), le sch´ema de type contraint le plus g´en´eral et valide pour la fonction cast est :

∀γ1γ2γ3γ4.[γ1= γ2⇒ γ3= γ4].eq γ1γ2→ γ3→ γ4

Cependant, cette forme de sch´emas de type ne nous semblent pas ad´equate pour un langage de programmation car elle fait apparaˆıtre des contraintes dont les formes r´esolues sont tr`es lourdes et dont la r´esolution ne peut pas ˆetre effectu´ee de tˆete  par le programmeur.

3.2.3 Solution retenue pour l’inf´erence de types

MLGX, une dose d’explicite pour retrouver la principalit´e

La section pr´ec´edente a montr´e dans quelle mesure la d´etermination des ´egalit´es de type et leur utilisation ´etaient sujets `a des choix interd´ependants. Le caract`ere implicite de la r`egle de conversion

34 3. Extension de ML par des GADT

et la nature disjonctive de l’implication sont `a l’origine de ce non-d´eterminisme. C’est pourquoi nous choisissons de supprimer ces deux traits du langage en ´elaborant un langage plus explicite que nous appelons MLGX.

Nous supprimons tout d’abord la r`egle de conversion implicite au profit d’une explicitation syn- taxique de l’utilisation des ´egalit´es de type. Pour cela, on introduit un op´erateur de coercion de la forme :

(t : τ1 . τ2)

Il signifie au syst`eme de type qu’un changement explicite du type d’un terme est n´ecessaire en indiquant qu’un terme dont le type est τ1doit ˆetre vu comme un terme de type τ2. Bien ´evidemment,

cette construction est accept´ee par le syst`eme de type seulement si les ´egalit´es de type du contexte ´etablissent effectivement l’´egalit´e τ1= τ2.

Nous supprimons ensuite la n´ecessit´e des implications de type dans le langage de contraintes en

for¸cant l’expression observ´ee par un pattern matching `a ˆetre annot´ee par un type rigide ou connu.

D`es lors, on extrait des ´egalit´es de type simplement en confrontant le type de chaque motif et ce type connu. Cette confrontation est locale et peut ˆetre effectu´e ind´ependamment de la v´erification et de la synth`ese des types. On peut donc d´eterminer en tout point du programme, dans une passe pr´eliminaire, les ´egalit´es de type qui y sont valides.

Dans MLGX, la fonction cast s’´ecrit :

let cast = ∀αβ.λe x.match (e : eq α β) of Eq → (x : α . β)

Pour pouvoir sp´ecifier le type rigide de la valeur analys´ee `a l’aide d’une annotation, on a introduit deux variables de type universellement quantifi´ees. Ensuite, on explicite la coercion du type α de la variable x en un type β.

Il n’y a pas d’ambigu¨ıt´e sur le sch´ema de type de cast, il s’agit de

∀αβ. eq α β → α → β

Plus g´en´eralement, cette dose d’explicitation permet `a MLGX de recouvrer des types principaux. Mieux encore, la suppression des implications de type dans le langage de contraintes permet de re- trouver l’inf´erence de types dans le style de Hindley-Milner. En effet, une fois v´erifi´ee la validit´e des coercions, on peut interpr´eter (x : α . β) comme l’application de la fonction identit´e (sans contenu calculatoire) de type α → β ce qui est un cas standard d’application de fonction en ML.

MLGX est d´ecrit dans le chapitre4de cette th`ese.

MLGI, une dose d’inf´erence locale pr´evisible pour all´eger le travail d’annotation

Le point noir de MLGX est la lourdeur des annotations de type. Pour s’en donner une id´ee, la figure8.3explicite les coercions et les annotations n´ecessaires pour ´ecrire la fonction eval dans MLGX. En observant ces annotations, on constate que certaines sont difficiles `a inf´erer tandis que d’autres se d´eduisent facilement du contexte. Plus pr´ecis´ement, on a vu pr´ec´edemment que d´eterminer le sch´ema de type d’une fonction utilisant des GADT est tr`es complexe1. L’annotation soulign´ee dans la figure

devrait donc ˆetre requise. Par contre, une fois cette annotation connue, les annotations en gris sont d´eduites facilement. Notons la pr´esence de variables de type dans les motifs, comme dans le motif  Pair β1β2a b . Il s’agit d’une introduction de variables de type universellement quantifi´ees dont le scope est limit´e au corps de la branche de ce motif.

La solution propos´ee pour d´echarger le programmeur de ce fardeau qu’est l’annotation est un algorithme d’inf´erence locale (aussi appel´e ´elaboration) qui va ins´erer automatiquement des coercions