Ich bin neu in der Theorie der Programmiersprache. Ich habe mir einige Online-Vorlesungen angesehen, in denen der Kursleiter behauptete, eine Funktion mit polymorphem Typ forall t: Type, t->t
sei die Identität, erklärte aber nicht, warum. Kann mir jemand erklären warum? Vielleicht ein Beweis für den Anspruch aus ersten Grundsätzen.
programming-languages
type-theory
abhishek
quelle
quelle
Antworten:
Das erste, was zu beachten ist, ist, dass dies nicht unbedingt wahr ist. Zum Beispiel könnte eine Funktion mit diesem Typ, abhängig von der Sprache, neben der Identitätsfunktion: 1) eine Endlosschleife
null
ausführen , 2) einen Zustand mutieren, 3) zurückgeben , 4) eine Ausnahme auslösen, 5) einige E / A-Vorgänge ausführen, 6) einen Threadcall/cc
verzweigen, um etwas anderes zu tun, 7) Spielereien machen, 8) etwas wie Java verwendenObject.hashCode
, 9) Reflektion verwenden, um festzustellen, ob der Typ eine Ganzzahl ist, und wenn ja, inkrementieren, 10) Reflektion verwenden, um den Aufrufstapel zu analysieren und Tun Sie etwas, das auf dem Kontext basiert, in dem es aufgerufen wird. 11) Wahrscheinlich viele andere Dinge und mit Sicherheit beliebige Kombinationen der obigen.Die Eigenschaft, die zu dieser Parametrizität führt, ist also eine Eigenschaft der gesamten Sprache, und es gibt stärkere und schwächere Variationen davon. Für viele der in der Typentheorie untersuchten formalen Kalküle kann keines der oben genannten Verhaltensweisen auftreten. Beispielsweise kann für das System F / der reine polymorphe Lambda-Kalkül, in dem die Parametrizität zum ersten Mal untersucht wurde, keines der obigen Verhalten auftreten. Es hat einfach nicht Ausnahmen, wandelbar Zustand
null
,call/cc
, I / O, Reflexion, und es normalisiert stark , so kann es nicht für immer Schleife. Wie Gilles in einem Kommentar erwähnte, ist die Veröffentlichung Theorems for free!von Phil Wadler ist eine gute Einführung in dieses Thema und seine Referenzen werden weiter in die Theorie eingehen, insbesondere in die Technik der logischen Beziehungen. In diesem Link sind auch einige andere Artikel von Wadler zum Thema Parametrizität aufgeführt.Da Parametrizität eine Eigenschaft der Sprache ist, müssen Sie zuerst die Sprache formell artikulieren und dann ein relativ kompliziertes Argument vorlegen, um dies zu beweisen. Das informelle Argument für diesen speziellen Fall unter der Annahme, dass wir uns im polymorphen Lambda-Kalkül befinden, ist, dass wir, da wir nichts darüber
t
wissen, keine Operationen an der Eingabe ausführen können (z. B. können wir sie nicht inkrementieren, weil wir nicht wissen, ob dies der Fall ist) eine Zahl) oder erstellen Sie einen Wert dieses Typs (nach allem, was wir wissent
=Void
, einen Typ ohne Werte). Die einzige Möglichkeit, einen Wert vom Typ zu erzeugen, besteht darin, den Wertt
zurückzugeben, der uns gegeben wurde. Andere Verhaltensweisen sind nicht möglich. Eine Möglichkeit, dies zu erkennen, besteht darin, eine starke Normalisierung zu verwenden und zu zeigen, dass es nur einen normalen Formterm dieses Typs gibt.quelle
Der Beweis der Behauptung ist recht komplex, aber wenn Sie das wirklich wollen, können Sie Reynolds 'Originalarbeit zu diesem Thema lesen.
Die Schlüsselidee ist, dass es für parametrisch polymorphe Funktionen gilt, wobei der Körper einer polymorphen Funktion für alle monomorphen Instanzen der Funktion gleich ist. In einem solchen System können keine Annahmen über den Typ eines Parameters vom polymorphen Typ getroffen werden. Wenn der einzige Wert im Gültigkeitsbereich einen generischen Typ hat, hat dies nichts mit ihm zu tun, als ihn zurückzugeben oder ihn an andere Funktionen weiterzugeben, die Sie verwenden. ve defined, dass wiederum nichts anderes tun kann, als es zurückzugeben oder zu übergeben .. .etc. Am Ende können Sie also nur eine Reihe von Identitätsfunktionen ausführen, bevor Sie den Parameter zurückgeben.
quelle
Lassen Sie mich mit all den Vorbehalten, die Derek erwähnt, und unter Ignorierung von Paradoxien, die sich aus der Verwendung der Mengenlehre ergeben, einen Beweis im Geiste von Reynolds / Wadler skizzieren.
Eine Funktion des Typs:
ist eine Familie von Funktionen die nach Typ indiziert sind . tft t
Die Idee ist, dass wir, um polymorphe Funktionen formal zu definieren, Typen nicht als Mengen von Werten behandeln sollten, sondern als Relationen. Grundtypen wie
Int
induzieren Gleichheitsrelationen - zB zweiInt
Werte hängen zusammen, wenn sie gleich sind. Funktionen sind verwandt, wenn sie verwandte Werte verwandten Werten zuordnen. Der interessante Fall sind polymorphe Funktionen. Sie ordnen verwandte Typen verwandten Werten zu.In unserem Fall wollen wir eine Beziehung zwischen zwei polymorphen Funktionen und vom Typ herstellen:gf g
Angenommen, Typ ist mit Typ . Die erste Funktion bildet den Typ auf einen Wert ab - hier ist der Wert selbst eine Funktion vom Typ . Die zweite Funktion bildet den Typ auf einen anderen Wert vom Typ . Wir sagen, dass mit wenn die Werte und zusammenhängen. Da diese Werte selbst Funktionen sind, sind sie verknüpft, wenn sie verknüpfte Werte verknüpften Werten zuordnen.t f s f s s → s t g t t → t f g f s g ts t f s fs s→s t gt t→t f g fs gt
Der entscheidende Schritt ist die Verwendung des Reynolds'schen Parametrizitätssatzes, der besagt, dass jeder Begriff in einer Beziehung zu sich selbst steht. In unserem Fall ist die Funktionfs ft
f
auf sich selbst bezogen. Mit anderen Worten, wenns
zu verwandt istt
, wird auch im Zusammenhang .f tWir können nun eine beliebige Beziehung zwischen zwei Typen auswählen und diesen Satz anwenden. Lassen Sie uns den ersten Typ als Einheitentyp auswählenf() ft f t f t i d tf() ft Karte muss ft idt
()
, der nur einen Wert hat, der auch als bezeichnet wird()
. Wir werden den zweiten Typt
willkürlich aber nicht leer lassen. Wählen wir eine Beziehung zwischen()
undt
, um einfach ein Paar zu sein((), c)
, wobeic
ein Wert des Typs vorliegtt
(eine Beziehung ist nur eine Teilmenge des kartesischen Produkts von Mengen). Der Parametrizitätssatz besagt, dass auf . Sie müssen verwandte Werte verwandten Werten zuordnen. Die erste Funktion hat nicht viel Auswahl, sie muss den einzigen Wert zurückmappen . Daher ist die zweite Funktion f t f (()
()
c
aufc
(die nur Werte in Bezug auf()
). Dac
es völlig willkürlich ist, schließen wir, dass und, da es völlig willkürlich ist, ist .t
f
id
Weitere Details finden Sie in meinem Blog .
quelle
BEARBEITEN: Ein Kommentar oben hat das fehlende Stück zur Verfügung gestellt. Einige Leute spielen absichtlich mit unvollständigen Sprachen. Solche Sprachen interessieren mich ausdrücklich nicht. Eine wirklich brauchbare Sprache, die nicht vollständig ist, ist verrückt schwer zu entwerfen. Der ganze Rest geht auf das ein, was passiert, wenn versucht wird, diese Theoreme auf eine vollständige Sprache anzuwenden.
Falsch!
Dabei
is
vergleicht der Operator zwei Variablen auf Referenzidentität. Das heißt, sie enthalten den gleichen Wert. Kein äquivalenter Wert, gleicher Wert. Funktionenf
undg
sind per definitionem äquivalent, aber sie sind nicht gleich.Wenn diese Funktion selbst übergeben wird, gibt sie etwas anderes zurück. Andernfalls wird die Eingabe zurückgegeben. Das etwas andere hat den gleichen Typ wie sich selbst, daher kann es ersetzt werden. Mit anderen Worten,
f
ist nicht die Identität, weilf(f)
zurückkehrtg
, während die Identität zurückkehren würdef
.Damit der Satz gilt, muss er die lächerliche Fähigkeit annehmen, zu reduzieren
Wenn Sie davon ausgehen möchten, dass Sie davon ausgehen können, dass die viel einfachere Typinferenz verarbeitet werden kann.
Wenn wir versuchen, die Domäne einzuschränken, bis der Satz gilt, müssen wir sie schrecklich weit einschränken.
raise
und neinexit
. Jetzt werden wir gezwungen.nil
. Das wird langsam problematisch. Wir haben keine Möglichkeiten mehr, mit 1 / 0.³ umzugehenDie Existenz der beiden letzten Einschränkungen hat die Sprache lahmgelegt. Während Turing noch vollständig ist, besteht die einzige Möglichkeit, allgemeine Aufgaben zu lösen, darin, eine innere Plattform zu simulieren, die eine Sprache mit geringeren Anforderungen interpretiert.
¹ Wenn Sie glauben, dass der Compiler diesen herleiten kann, probieren Sie diesen aus
² Der Beweis, dass der Compiler dies nicht kann, hängt von der Blendung ab. Wir können mehrere Bibliotheken verwenden, um sicherzustellen, dass der Compiler die Schleife nicht gleichzeitig sehen kann. Außerdem können wir immer etwas konstruieren, bei dem das Programm funktionieren würde, das jedoch nicht kompiliert werden konnte, da der Compiler die Induktion im verfügbaren Speicher nicht ausführen kann.
³ Jemand denkt, Sie können diesen Rückgabewert auf Null setzen, ohne dass willkürliche generische Typen den Wert Null zurückgeben. Das zahlt eine fiese Strafe, für die ich keine wirksame Sprache gesehen habe, die es bezahlen kann.
darf nicht kompilieren. Das grundlegende Problem ist, dass die Laufzeit-Array-Indizierung nicht mehr funktioniert.
quelle
foil
Quantifizierer überhaupt?) Dies ist überhaupt nicht hilfreich.