Inférence de type avec les types de produits

15

Je travaille sur un compilateur pour un langage concaténatif et je voudrais ajouter un support d'inférence de type. Je comprends Hindley – Milner, mais j'ai appris la théorie des types au fur et à mesure, donc je ne sais pas comment l'adapter. Le système suivant est-il solide et inférable?

Un terme est un littéral, une composition de termes, une citation d'un terme ou une primitive.

e:: =X|ee|[e]|

Tous les termes désignent des fonctions. Pour deux fonctions et , , c'est-à-dire que la juxtaposition désigne la composition inverse. Les littéraux désignent les fonctions niladiques.e 2 e 1e1e2e1e2=e2e1

Les termes autres que composition ont des règles de type de base:

X:ι[Lit]Γe:σΓ[e]:α.ασ×α[Quot],α pas libre dans Γ

Les règles d'application sont particulièrement absentes, car les langues concaténatives en sont dépourvues.

Un type est soit un littéral, une variable de type, soit une fonction de piles en piles, où une pile est définie comme un tuple imbriqué à droite. Toutes les fonctions sont implicitement polymorphes par rapport au «reste de la pile».

τ:: =ι|α|ρρρ:: =()|τ×ρσ:: =τ|α.σ

C'est la première chose qui semble suspecte, mais je ne sais pas exactement ce qui ne va pas.

Pour faciliter la lisibilité et réduire les parenthèses, je suppose que dans les schémas de types. Je vais également utiliser une lettre majuscule pour une variable désignant une pile, plutôt qu'une seule valeur.uneb=b×(une)

Il existe six primitives. Les cinq premiers sont assez inoffensifs. dupprend la valeur la plus élevée et en produit deux copies. swapchange l'ordre des deux premières valeurs. popignore la valeur supérieure. quoteprend une valeur et produit une citation (fonction) qui la renvoie. applyapplique un devis à la pile.

up::UNEb.UNEbUNEbbswunep::UNEbc.UNEbcUNEcbpop::UNEb.UNEbUNEquote::UNEb.UNEbUNE(C.CCb)unepply::UNEB.UNE(UNEB)B

Le dernier combinateur compose,, doit prendre deux citations et retourner le type de leur concaténation, c'est-à-dire . Dans le langage concaténatif de type statique Cat , le type de est très simple.[e1][e2]compose=[e1e2]compose

compose::ABCD.A(BC)(CD)A(BD)

Cependant, ce type est trop restrictif: il nécessite que la production de la première fonction corresponde exactement à la consommation de la seconde. En réalité, vous devez assumer des types distincts, puis les unifier. Mais comment écririez-vous ce type?

compose::ABCDE.A(BC)(DE)A

Si vous laissez dénoter une différence de deux types, alors je pense que vous pouvez écrire le type de correctement.compose

compose::ABCDE.A(BC)(DE)A((DC)B((CD)E))

Ceci est encore relativement simple: composeprend une fonction et une . Son résultat consomme au sommet de la consommation de non produite par , et produit au sommet de la production de non consommée par . Cela donne la règle de la composition ordinaire.f 2 : D E B f 2 f 1 D f 1 f 2f1:BCf2:DEBf2f1Df1f2

Γe1:UNEB.UNEBΓe2:C.CΓe1e2:((CB)UNE((BC)))[Comp]

Cependant, je ne sais pas si cet hypothétique correspond en fait à quelque chose, et je le poursuis en rond depuis assez longtemps pour que je pense avoir pris un mauvais virage. Serait-ce une simple différence de tuples?

UNE.()UNE=()UNE.UNE()=UNEUNEBC.UNEBC=B ssi UNE=Cautrement=indéfini

Y a-t-il quelque chose d'horriblement cassé que je ne vois pas, ou suis-je sur quelque chose comme la bonne voie? (J'ai probablement quantifié à tort certaines de ces choses et j'apprécierais également les correctifs dans ce domaine.)

Jon Purdy
la source
Comment utilisez-vous les variables dans votre grammaire? Cette question devrait vous aider à gérer le "sous-typage" dont vous semblez avoir besoin.
jmad
1
@jmad: Je ne suis pas sûr de comprendre la question. Les variables de type sont juste là pour définir formellement des schémas de types, et le langage lui-même n'a pas du tout de variables, juste des définitions, qui peuvent être [mutuellement] récursives.
Jon Purdy
C'est suffisant. Pouvez-vous dire pourquoi (peut-être avec un exemple) la règle pour composeest trop restrictive? J'ai l'impression que c'est bien comme ça. (par exemple la restriction pourrait être gérée par unification comme pour une application similaire dans le λ-calcul)C=
jmad
@jmad: Bien sûr. Considérez twicedéfini comme dup compose apply, qui prend une citation et l'applique deux fois. [1 +] twicec'est bien: vous composez deux fonctions de type . Mais ce n'est pas le cas: si , le problème est que , donc l'expression est interdite même si elle doit être valide et avoir taper . La solution est bien sûr de mettre le qualificatif au bon endroit, mais je me demande principalement comment écrire réellement le type de sans définition circulaire. A b .ιι[pop] twiceA AUNEb.F1,F2:UNEbUNEA b .UNEUNEbUNEb.UNEbbUNEcompose
Jon Purdy

Réponses:

9

Le type de rang 2 suivant semble être suffisamment général. Il est beaucoup plus polymorphe que le type proposé dans la question. Ici, la quantification variable sur des morceaux contigus de pile, qui capture les fonctions multi-arguments.

composer:UNEBCδ.δ (α.α UNEαB) (β.β BβC)δ (γ.γ UNEγC)

Les lettres grecques sont utilisées pour les autres variables de la pile à des fins de clarté uniquement.

Il exprime les contraintes que la pile de sortie du premier élément de la pile doit être la même que la pile d'entrée du deuxième élément. Instancier de manière appropriée la variable pour les deux arguments est le moyen de faire fonctionner correctement les contraintes, plutôt que de définir une nouvelle opération, comme vous le proposez dans la question.B

La vérification de type des types de rang 2 est indécidable en général, je pense, bien qu'un travail ait été fait qui donne de bons résultats dans la pratique (pour Haskell):

  • Simon L. Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, Mark Shields: inférence de type pratique pour les types de rang arbitraire. J. Funct. Programme. 17 (1): 1-82 (2007)

La règle de type pour la composition est simplement:

Γe1:α.α UNEα BΓe1:α.α Bα CΓe1 e2:α.α UNEα C

Pour que le système de typage fonctionne en général, vous avez besoin de la règle de spécialisation suivante:

Γe:α.α UNEα BΓe:α.C UNEα C B
Dave Clarke
la source
Merci, ceci était vraiment utile. Ce type est correct pour les fonctions d'un seul argument, mais il ne prend pas en charge plusieurs arguments. Par exemple, dup +devrait avoir le type car a le type . Mais l'inférence de type en l'absence d'annotations est une exigence absolue, donc je dois clairement revenir à la planche à dessin. J'ai une idée pour une autre approche à poursuivre, cependant, et bloguerai à ce sujet si cela fonctionne. ιι+ιιι
Jon Purdy
1
Les types de pile quantifient sur des fragments de pile, il n'y a donc aucun problème à gérer deux fonctions d'argument. Je ne sais pas comment cela s'applique dup +, car cela n'utilise pas la composition, comme vous l'avez défini ci-dessus.
Dave Clarke
Euh, c'est vrai, je voulais dire [dup] [+] compose. Mais j'ai lu comme ; dites ; alors vous avez et non . L'imbrication n'est pas correcte, sauf si vous retournez la pile pour que le haut soit le dernier élément (le plus profond imbriqué). B × α B = ι × ι ( ι × ι ) × α ι × ( ι × α )αBB×αB=ι×ι(ι×ι)×αι×(ι×α)
Jon Purdy
Je construis peut-être ma pile dans la mauvaise direction. Je ne pense pas que l'imbrication importe, tant que les paires qui constituent la pile n'apparaissent pas dans le langage de programmation. (Je prévois de mettre à jour ma réponse, mais je dois d'abord faire un peu de recherche.)
Dave Clarke
Oui, l'imbrication est à peu près un détail d'implémentation.
Jon Purdy