Il est malheureux qu'une grande partie de la littérature sur le sujet soit très dense. Moi aussi j'étais à ta place. J'ai eu ma première introduction au sujet dans Langages de programmation: applications et interprétation
http://www.plai.org/
Je vais essayer de résumer l'idée abstraite suivie de détails que je n'ai pas trouvés immédiatement évidents. Premièrement, l'inférence de type peut être pensée pour générer puis résoudre des contraintes. Pour générer des contraintes, vous parcourez l'arborescence de syntaxe et générez une ou plusieurs contraintes sur chaque nœud. Par exemple, si le nœud est un +
opérateur, les opérandes et les résultats doivent tous être des nombres. Un nœud qui applique une fonction a le même type que le résultat de la fonction, et ainsi de suite.
Pour un langage sans let
, vous pouvez résoudre aveuglément les contraintes ci-dessus par substitution. Par exemple:
(if (= 1 2)
1
2)
ici, nous pouvons dire que la condition de l'instruction if doit être booléenne, et que le type de l'instruction if est le même que le type de ses clauses then
and else
. Puisque nous connaissons 1
et 2
sommes des nombres, par substitution, nous savons que l' if
énoncé est un nombre.
Là où les choses deviennent désagréables, et ce que je n'ai pas pu comprendre pendant un moment, c'est que:
(let ((id (lambda (x) x)))
(id id))
Ici, nous sommes liés id
à une fonction qui renvoie tout ce que vous avez passé, autrement connu sous le nom de fonction d'identité. Le problème est que le type de paramètre de la fonction x
est différent à chaque utilisation de id
. La seconde id
est une fonction de type a -> a
, où a
peut être n'importe quoi. Le premier est de type (a -> a) -> (a -> a)
. Ceci est connu sous le nom de let-polymorphisme. La clé est de résoudre les contraintes dans un ordre particulier: commencez par résoudre les contraintes pour la définition de id
. Ce sera a -> a
. Ensuite, des copies fraîches et séparées du type de id
peuvent être substituées dans les contraintes pour chaque lieu id
, par exemple a2 -> a2
et a3 -> a3
.
Cela n'a pas été facilement expliqué dans les ressources en ligne. Ils mentionneront l'algorithme W ou M, mais pas comment ils fonctionnent en termes de résolution des contraintes, ou pourquoi il ne barf sur let-polymorphisme: chacun de ces algorithmes applique un ordre sur la résolution des contraintes.
J'ai trouvé cette ressource extrêmement utile pour lier les algorithmes W, M et le concept général de génération et de résolution de contraintes. C'est un peu dense, mais meilleur que beaucoup:
http://www.cs.uu.nl/research/techreps/repo/CS-2002/2002-031.pdf
La plupart des autres articles sont également intéressants:
http://people.cs.uu.nl/bastiaan/papers.html
J'espère que cela aidera à clarifier un monde quelque peu trouble.