Daher kenne ich zwei Hauptstrategien für einen höherrangigen Polymorphismus in einer Sprache:
- Polymorphismus im System-F-Stil, bei dem Funktionen explizit typisiert werden und die Instanziierung explizit über die Typanwendung erfolgt. Diese Systeme können beeindruckend sein.
- Subtypisierungsbasierter Polymorphismus, wobei ein polymorpher Typ ein Subtyp aller seiner Instanziierungen ist. Um eine entscheidbare Subtypisierung zu haben, muss der Polymorphismus prädikativ sein. Dieses Dokument enthält ein Beispiel für ein solches System.
Einige Sprachen, wie Haskell, weisen jedoch einen unprädikativen höherrangigen Polymorphismus ohne explizite Typanwendungen auf.
Wie ist das möglich? Wie kann die Typprüfung "wissen", wann ein Typ ohne explizite Instanziierung oder Umwandlung und ohne den Begriff der Subtypisierung instanziiert werden muss?
Oder ist Typchecking in einem solchen System überhaupt entscheidbar? Ist dies ein Fall, in dem eine Sprache wie Haskell etwas Unentscheidbares implementiert, das für die Anwendungsfälle der meisten Menschen funktioniert?
BEARBEITEN:
Um es klar auszudrücken, ich interessiere mich für die Verwendung und nicht für Definitionen von polymorph typisierten Werten.
Nehmen wir zum Beispiel an, wir haben:
f : forall a . a -> a
g : (forall a . a -> a) -> Int
val = (g f, f True, f 'a')
Wie können wir wissen, dass wir instanziieren müssen, f
wenn es angewendet wird, aber nicht, wenn es als Argument angegeben wird?
Oder um uns von Funktionstypen zu trennen:
f : forall a . a
g : (forall a . a) -> Int
val = (g f, f && True, f + 0)
Hier können wir nicht einmal zwischen der Verwendung f
als Anwenden und Übergeben unterscheiden: Es wird instanziiert, wenn es als Argument an &&
und übergeben wird +
, aber nicht g
.
Wie kann ein theoretisches System diese beiden Fälle ohne die magische Regel "Sie können jeden polymorphen Typ in seine Instanz konvertieren" unterscheiden? Oder können wir mit einer solchen Regel wissen, wann wir sie anwenden müssen, um die Entscheidbarkeit zu bewahren?
\f -> (f True, f 'a')
wird keine Typprüfung durchgeführt, auch wenn ihr der Typ zugewiesen werden kann(forall t. t->t) -> (Bool, Char)
g
dass sie einen Polytyp erwartet und die Instanziierung von verhindertf
.Antworten:
Die Einführung des Dunfield & Krishnaswami-Papiers bezieht sich auf die praktische Typinferenz für Typen mit beliebigem Rang
Beim System-F-ish-Ansatz gibt es auch eine "Subtypisierungs" -Relation. Siehe Abschnitt 3.3 Subsumtion.
Ich möchte auch betonen, dass Haskell keine aussagekräftigen Typen (oder Schlussfolgerungen für sie) hat. Hinweise finden Sie unter: https://mail.haskell.org/pipermail/ghc-devs/2016-September/012940.html .
Dh
id id
wird immer als ausgearbeitetnicht
Letzteres können Sie jedoch explizit schreiben, wenn Sie es aktivieren
ImpredicativeTypes
.quelle
So überprüfen Sie die Anwendung einer Funktion wie
g : (forall a. a -> a) -> Int
zuf
, müssen wir das überprüfenf : forall a. a -> a
.Anstatt Quantifizierer abzugleichen (was ziemlich spröde wäre), führen wir beispielsweise eine frische, starre (dh nicht unifizierbare) Variable ein
a1
, und wir müssen dies überprüfenf : a1 -> a1
, und jetzt können wir wie gewohnt weitermachen undf
beia1
(modulo Additional) instanziieren prüft, oba1
dies nicht dem Anwendungsbereich entgeht).Der eigentliche Algorithmus ist in dem verknüpften Artikel phadej beschrieben.
Das allgemeine Problem der Typinferenz bei Vorhandensein eines höherrangigen Polymorphismus bleibt unentscheidbar. Bei vollständigen Typanmerkungen wird es jedoch zu einem (meistens?) Entscheidbaren Problem bei der Typprüfung . Der GHC-Algorithmus muss daher unvollständig sein, versucht jedoch, mit Hilfe einiger Anmerkungen vom spärlichen Typ so viel Boden wie möglich in der Mitte dieser beiden Situationen abzudecken.
quelle