Polymorphisme
L’identité Fun x -> x a plusieurs types.
Type principal X -> X.
Mais Let id = Fun x -> x In id id
n’est pas typable.
|
Ce qui conduit finalement à l’équation X=X -> X, qui n’a pas de solution.
Et pourtant, puisque id possède tous les types A -> A, les types suivants sont possibles pour la première et la seconde occurrence de id.
|
|
Et l’application id id a pour type Z -> Z.
Autoriser des instances différentes σ(A) d’un même type.
Enrichir les types :
|
|
|
|
Ajouter une règle « d’élimination » de ∀.
|
On parle aussi d’instanciation (de la variable liée X).
(Il nous faut aussi une règle « d’introduction » de ∀, mais laissons cela de côté pour le moment).
Les types contienent maintenant un lieur (∀), comme les termes (Fun etc.)
Ceci complique la définition de la substitution qui doit éviter les captures des variables libres.
|
|
|
|
Et pour la substitution
|
|
Dans l’environnement E = id:∀ X[X -> X], on souhaite typer l’application id id.
On y arrive par exemple ainsi.
|
Les deux occurrences de id donnent lieu à deux instanciations différentes.
Exemple : composition de fonctions.
La liste de X est un type noté (∀'a.
)'a list
en Caml.
Défini par deux « constructeurs »
[]
), de type (∀'a.
)'a list
.
::
), de type (∀'a.
)'a -> 'a list -> 'a list
.
Toutes les fonctions qui ne « regardent pas » directement les éléments
(de type 'a
) sont polymorphes.
Calcule f(x1,f(x2,… f(xn,y0))).
Ou (plus classique) un tri :
Des constructions classiques du λ-calcul (PCF réduit à Fun, application et variables) sont typables.
Par exemple, l’encodage suivant des entiers, montre que nos constantes et opérateurs sur les entiers sont inutiles (dumoins en théorie).
Fun f -> |
|
Type des entiers N=∀ X[(X -> X) -> X -> X]
Certaines primitives (toutes ?) sur les entiers sont alors typables:
Revenons sur le typage de succ
N=∀ X[(X -> X) -> X -> X] |
|
On veut conclure
|
Et donc (variable muette), succ de type N -> N.
La règle de généralisation.
|
Pour succ la généralisation est légitime, car Z est quelconque.
Pensons à la locution « Soit n un entier quelconque ».
Par exemple, pour a et b quelconques, (a+b)2 = a2 + 2ab + b2 et donc
∀ a, b ∈ ℕ2, (a+b)2 = a2 + 2ab + b2 |
Pour exprimer « X quelconque » : X n’apparaît pas dans E.
En déduction, cela correspond à l’absence d’hypothèses.
Par ex. de n pair alors n2 pair, on ne peut pas déduire que tous les carrés sont pairs.
On peut typer Obj.magic : 'a -> 'b
.
|
En effet,
Se réduit en 1 2
, c-à-d une erreur de type à l’exécution.
|
|
|
|
|
De Girard (1970, logicien) et Reynols (1975, informaticien).
Notre présentation est emprunté à Wells (1994), qui a prouvé que la synthèse de type est indécidable.
Inutiles, si on considère que les constantes c ont des types présents dans l’environnement de départ.
|
Alors, t1 + t2 se comprend comme (+) t1 t2.
Alors, Ifz t1 Then t2 Else t3 se comprend comme ifz t1 t2 t3
On suppse donné, un combinateur de point fixe fix de type ∀ X[(X -> X) -> X].
Alors Fix f -> t se type comme fix appliqué à (Fun f -> t).
Au lieu d’ajouter une règle du style :
|
On se contente des règles du Fun et de l’application.
|
Une telle simplification n’est pas négligeable, quand on code l’inférence.
L’inférence de type du système F n’est pas décidable.
Pour généraliser le type A dans l’env. E on applique la fonction de généralisation : gen(A,E)
gen(A,E) = ∀ X1⋯Xn[A], où { X1,…,Xn } = F(A) ∖ F(E) (Fpour variables libres) |
Types (A, B etc.), comme avant !
|
|
|
Schémas de type (S)
|
Les schémas sont réservés aux environnements de typage
[⋯ ; x:S; ⋯] |
Note : un type ordinaire A est représenté par le schéma ∀ ∅[A].
|
|
|
|
Et if:∀ X[Nat -> X -> X], fix:∀ X[(X -> X) -> X] etc.
Il devient délicat de procéder à l’inférence en deux phases
Car le language des équations devient plus riche que A=B (il doit comprendre des schémas de type).
Mais la résolution immédiate fonctionne.
Revoyons l’idée : résoudre les équations dès qu’elles sont posées.
On avait :
| (X frais) |
On a :
| (X frais) |
La forme générale du jugement est
E, σ ⊢ t ↝ A, σ′ |
Le composant σ représente la solution courante des équations « vues ».
Propriété : σ′ étend σ (σ′ = σ″ ∘ σ) et σ′(E) ⊢ t:σ′(A) (et même type principal).
Soit σ la solution d’un problème d’unification. C’est à dire σ = { X1 ↦ A1, …, Xn ↦ An }, avec :
Et soit une nouvelle équation B = C.
On veut résoudre les équation E = { B = C } ∪ σ.
On pourrait simplement poser mgu(E), mais ça serait dommage d’oublier que σ est déjà résolu.
Pour résoudre { B = C, X1 ↦ A1, …, Xn ↦ An }.
Cela s’abrège en mgu[σ(B) = σ(C)] ∘ σ.
La notation ∘ exprimant les étapes 3. et 4. mais de façon trop générale (quid si Xi = Yj ?).
|
pow est de type P, n est de type N (E est [x:X, pow:P]).
σ″ = [Y↦Nat, P↦(Nat -> Nat), N↦Nat] |
E, id ⊢ (Ifz n Then 1 Else 2*pow (n-1)) ↝ Nat, σ″ |
mgu[σ″(P) = σ″(N -> Nat)] ∘ σ″ |
|
|
|
|
Et les constantes ifz de type ∀ X[Nat -> X -> X], fix de type ∀ X[(X -> X) -> X] etc.
Inférons d’abord le type de Fun x -> x. Rien de bien sorcier.
|
On a gen(id(X -> X),id(∅)) = ∀ X[X -> X]. L’application id id est à typer dans E = [id:∀ X[X -> X]].
|
La résolution donne [Y↦(Z -> Z), W↦(Z -> Z)].
Soit à typer Fun f -> Fun x -> Let y = f x In y+1.
{ Y ↦ Nat, Z ↦ Nat -> Nat, F ↦ X -> Nat } |
{ W ↦ Nat, Y ↦ Nat, Z ↦ Nat -> Nat, F ↦ X -> Nat } |
ML est un language fondée sur le typage de Damas-Milner. La synthèse de type (et donc l’existence de typage principaux) est fondamentale.
Cela pose des problèmes et freine un peu les extensions possibles
fun x -> x+x
?).
=:'a -> 'a -> bool
) n’est pas
bien satisfaisant, si 'a
vaut 'b -> 'c
…Il y a encore de la recherche sur ces questions, les type classes de Haskell proposent une solution intéressante aux deux premiers problèmes.
Le polymorphisme (paramétrique) existe en Java !
Mais c’est un peu plus explicite qu’en ML. (En échange, le sous-typage est possible.)
Se montre suffisant pour les structure de données (Set<E>
etc.)
Ce document a été traduit de LATEX par HEVEA