Typinferenz mit Produkttypen

15

Ich arbeite an einem Compiler für eine verkettete Sprache und möchte Unterstützung für Typinferenz hinzufügen. Ich verstehe Hindley-Milner, aber ich habe die Typentheorie im Laufe der Zeit gelernt, daher bin ich mir nicht sicher, wie ich sie anpassen soll. Ist das folgende System solide und eindeutig ableitbar?

Ein Begriff ist ein Literal, eine Zusammensetzung von Begriffen, ein Zitat eines Begriffs oder ein Primitiv.

e::=x|ee|[e]|

Alle Begriffe bezeichnen Funktionen. Für zwei Funktionen e1 und e2 , e1e2=e2e1 , das heißt, die Nebeneinanderstellung bezeichnet die umgekehrte Zusammensetzung. Literale bezeichnen niladische Funktionen.

Die Begriffe außer Komposition haben grundlegende Typregeln:

x:ι[Lit]Γe:σΓ[e]:α.ασ×α[Quot],α not free in Γ

Insbesondere fehlen Anwendungsregeln, da es an konkatenativen Sprachen mangelt.

Ein Typ ist entweder ein Literal, eine Typvariable oder eine Funktion von Stapel zu Stapel, wobei ein Stapel als rechtsverschachteltes Tupel definiert ist. Alle Funktionen sind implizit polymorph in Bezug auf den "Rest des Stapels".

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

Dies ist das erste, was verdächtig erscheint, aber ich weiß nicht genau, was daran falsch ist.

Um die Lesbarkeit zu verbessern und Klammern zu reduzieren, gehe ich davon aus, dass in Typschemata. Ich werde auch einen Großbuchstaben für eine Variable verwenden, die einen Stapel statt eines einzelnen Werts bezeichnet.ab=b×(a)

Es gibt sechs Grundelemente. Die ersten fünf sind ziemlich harmlos. dupNimmt den höchsten Wert und erstellt zwei Kopien davon. swapÄndert die Reihenfolge der beiden obersten Werte. popverwirft den obersten Wert. quoteNimmt einen Wert und erzeugt ein Zitat (eine Funktion), das ihn zurückgibt. applyWendet ein Zitat auf den Stapel an.

dup::Ab.AbAbbswap::Abc.AbcAcbpop::Ab.AbAquote::Ab.AbA(C.CCb)apply::AB.A(AB)B

Der letzte Kombinator compose, sollte zwei Zitate nehmen und die Art ihrer Verkettung zurück, das heißt, . In der statisch typisierten VerkettungsspracheCatist der Typ vonsehr einfach.[e1][e2]compose=[e1e2]compose

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

Dieser Typ ist jedoch zu restriktiv: Er erfordert, dass die Produktion der ersten Funktion genau mit dem Verbrauch der zweiten Funktion übereinstimmt . In Wirklichkeit muss man verschiedene Typen annehmen und sie dann vereinheitlichen. Aber wie würden Sie diesen Typ schreiben?

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

Wenn Sie lassen einen bezeichnen Unterschied von zwei Typen, dann ich denke , Sie die Art der schreiben kann composerichtig.

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

Dies ist immer noch relativ einfach: composenimmt eine Funktion und eine f 2 : D E . Sein Ergebnis verbraucht B oben auf dem Verbrauch von f 2 nicht erzeugt f 1 und erzeugt D oben auf die Herstellung von f 1 nicht verbraucht durch f 2 . Dies gibt die Regel für die gewöhnliche Zusammensetzung.f1:BCf2:DEBf2f1Df1f2

Γe1:AB.ABΓe2:CD.CDΓe1e2:((CB)A((BC)D))[Comp]

Ich weiß jedoch nicht, dass dieses hypothetische tatsächlich irgendetwas entspricht, und ich habe es lange genug im Kreis herumgejagt , sodass ich glaube, dass ich falsch abgebogen bin. Könnte es ein einfacher Unterschied von Tupeln sein?

A.()A=()A.A()=AABCD.ABCD=BD iff A=Cotherwise=undefined

Gibt es etwas Schreckliches daran, das ich nicht sehe, oder bin ich auf dem richtigen Weg? (Ich habe wahrscheinlich einige dieser Dinge falsch quantifiziert und würde auch Korrekturen in diesem Bereich begrüßen.)

Jon Purdy
quelle
Wie verwenden Sie Variablen in Ihrer Grammatik? Diese Frage soll Ihnen bei der Handhabung der "Untertypisierung" helfen, die Sie anscheinend benötigen.
jmad
1
@jmad: Ich bin nicht sicher, ob ich die Frage verstehe. Typvariablen sind nur dazu da, Typschemata formal zu definieren, und die Sprache selbst hat überhaupt keine Variablen, sondern nur Definitionen, die [gegenseitig] rekursiv sein können.
Jon Purdy
composeC=D
twicedup compose apply[1 +] twiceιι[pop] twiceAb.f1,f2:AbAEINEINbDaher ist der Ausdruck nicht zulässig, obwohl er gültig sein und einen Typ haben sollte EINb.EINbbEIN. The solution is of course to put the qualifier in the right place, but I’m mainly wondering how to actually write the type of compose without some circular definition.
Jon Purdy

Antworten:

9

The following rank-2 type

komponieren:EINBCδ.δ (α.α EINαB) (β.β BβC)δ (γ.γ EINγC)
scheint ausreichend allgemein zu sein. Es ist viel polymorpher als der in der Frage vorgeschlagene Typ. Hier wird die Variable über zusammenhängende Stapelstücke quantifiziert, wodurch Funktionen mit mehreren Argumenten erfasst werden.

Griechische Buchstaben werden nur aus Gründen der Übersichtlichkeit für die übrigen Stapelvariablen verwendet.

Es drückt die Einschränkungen aus, dass der Ausgabestapel des ersten Elements im Stapel mit dem Eingabestapel des zweiten Elements identisch sein muss. Variable entsprechend instanziierenB Denn mit den beiden eigentlichen Argumenten wird erreicht, dass die Einschränkungen ordnungsgemäß funktionieren, anstatt eine neue Operation zu definieren, wie Sie in der Frage vorschlagen.

Type checking rank-2 types is undecidable in general, I believe, though some work has been done that gives good results in practice (for Haskell):

  • Simon L. Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, Mark Shields: Practical type inference for arbitrary-rank types. J. Funct. Program. 17(1): 1-82 (2007)

The type rule for composition is simply:

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

To get the type system to work in general, you need the following specialisation rule:

Γe:α.α Aα BΓe:α.C Aα C B
Dave Clarke
quelle
Thanks, this was very helpful. This type is correct for functions of a single argument, but it doesn’t support multiple arguments. For instance, dup + should have type ιι because + has type ιιι. But type inference in the absence of annotations is an absolute requirement, so clearly I need to go back to the drawing board. I have an idea for another approach to pursue, though, and will blog about it if it works out.
Jon Purdy
1
The stack types quantify over stack fragments, so there is no problem dealing with two argument functions. I'm not sure how this applies to dup +, as that does not use compose, as you defined it above.
Dave Clarke
Er, right, I meant [dup] [+] compose. But I read αB as B×α; say B=ι×ι; then you have (ι×ι)×α and not ι×(ι×α). The nesting isn’t right, unless you flip the stack around so that the top is the last (deepest nested) element.
Jon Purdy
I may be building my stack in the wrong direction. I don't think the nesting matters, so long as the pairs building up the stack do not appear in the programming language. (I'm planning to update my answer, but need to do a little research first.)
Dave Clarke
Yeah, nesting is pretty much an implementation detail.
Jon Purdy