Aus dieser Referenz: Strikte Positivität
Die strenge Positivitätsbedingung schließt Erklärungen wie z
data Bad : Set where
bad : (Bad → Bad) → Bad
A B C
-- A is in a negative position, B and C are OK
Warum ist A negativ? Auch warum ist B erlaubt? Ich verstehe, warum C erlaubt ist.
type-theory
inductive-datatypes
Pushpa
quelle
quelle
A
Stapels verursachen und ihn schließlich explodieren lassen (in stapelbasierten Sprachen).Antworten:
Nun zu induktiven Datentypen.
In der Algebra ist es üblich, dass eine Operation eine endliche Anzahl von Argumenten akzeptiert, und in den meisten Fällen werden null (konstant), ein (unär) oder zwei (binär) Argumente verwendet. Es ist zweckmäßig, dies für Konstruktoren von Datentypen zu verallgemeinern. Angenommen, es
c
handelt sich um einen Konstruktor für einen DatentypT
:c
es sich um eine Konstante handelt, können wir sie als Funktionunit -> T
oder gleichwertig betrachten(empty -> T) -> T
.c
es unär ist, können wir es uns als eine FunktionT -> T
oder gleichwertig vorstellen(unit -> T) -> T
.c
es binär ist, können wir es uns als eine FunktionT -> T -> T
vorstellenT * T -> T
, oder gleichwertig oder gleichwertig(bool -> T) -> T
.c
mit sieben Argumenten wollten , könnten wir ihn als eine Funktion betrachten,(seven -> T) -> T
bei derseven
es sich um einen zuvor definierten Typ mit sieben Elementen handelt.c
der unendlich viele Argumente akzeptiert, das wäre eine Funktion(nat -> T) -> T
.Diese Beispiele zeigen, dass die allgemeine Form eines Konstruktors sein sollte
wo wir
A
die Arität von nennenc
und unsc
als einen KonstruktorA
vorstellen , der viele Argumente vom Typ verwendetT
, um ein Element von zu erzeugenT
.Hier ist etwas sehr Wichtiges: Die Aritäten müssen definiert werden, bevor wir sie definieren
T
, sonst können wir nicht sagen, was die Konstruktoren tun sollen. Wenn jemand versucht, einen Konstruktor zu habendann die Frage "Wie viele Argumente braucht
broken
man?" hat keine gute Antwort. Sie könnten versuchen, es mit "es braucht-T
viele Argumente" zu beantworten , aber das geht nicht, weilT
es noch nicht definiert ist. Wir könnten versuchen, aus dem Cunundrum herauszukommen, indem wir eine ausgefallene Festpunkttheorie verwenden, um einen TypT
und eine injizierende Funktion zu finden(T -> T) -> T
, und würden Erfolg haben, aber wir würden auch das Induktionsprinzip fürT
unterwegs brechen . Es ist also nur eine schlechte Idee, so etwas auszuprobieren.c
B
Tatsächlich können viele Konstrukteure auf diese Weise neu geschrieben werden, aber nicht alle, brauchen wir einen weiteren Schritt, nämlich sollten wir zulassen , dass hängen auf :
A
B
Dies ist die endgültige Form eines Konstruktors für einen induktiven Typ. Es ist auch genau das, was W-Typen sind. Die Form ist so allgemein, dass wir immer nur einen einzigen Konstruktor brauchen
c
! In der Tat, wenn wir zwei davon habendann können wir sie zu einem kombinieren
wo
Übrigens, wenn wir die allgemeine Form curry sehen, sehen wir, dass es äquivalent zu ist
Das ist näher an dem, was die Leute tatsächlich in Beweisassistenten aufschreiben. Die Proof-Assistenten ermöglichen es uns, die Konstruktoren auf bequeme Weise aufzuschreiben, aber diese entsprechen der obigen allgemeinen Form (Übung!).
quelle
Das erste Vorkommen von
Bad
wird als "negativ" bezeichnet, da es ein Funktionsargument darstellt, dh sich links vom Funktionspfeil befindet (siehe Rekursive Typen kostenlos von Philip Wadler). Ich denke, der Ursprung des Begriffs "negative Position" ergibt sich aus dem Begriff der Kontravarianz ("Kontra" bedeutet Gegenteil).Es ist nicht erlaubt, den Typ in einer negativen Position zu definieren, da man damit nicht terminierende Programme schreiben kann, dh eine starke Normalisierung schlägt in seiner Gegenwart fehl (mehr dazu weiter unten). Dies ist übrigens der Grund für den Namen der Regel "strikte Positivität": Wir erlauben keine negativen Positionen.
Wir erlauben das zweite Auftreten von,
Bad
da es keine Nichtbeendigung verursacht, und wir möchten den Typ, der definiert wird (Bad
), irgendwann in einem rekursiven Datentyp ( vor dem letzten Pfeil seines Konstruktors) verwenden.Es ist wichtig zu verstehen, dass die folgende Definition nicht gegen die strenge Positivitätsregel verstößt.
Die Regel gilt nur für Konstruktorargumente (
Good
in diesem Fall beide ) und nicht für einen Konstruktor selbst (siehe auch Adam Chlipalas " Zertifizierte Programmierung mit abhängigen Typen ").Ein weiteres Beispiel, das gegen strikte Positivität verstößt:
Vielleicht möchten Sie diese Antwort auch auf negative Positionen überprüfen .
Weitere Informationen zur Nichtbeendigung ... Die Seite, auf die Sie verwiesen haben, enthält einige Erklärungen (zusammen mit einem Beispiel in Haskell):
Hier ist ein analoges Beispiel in Ocaml, das zeigt, wie rekursives Verhalten implementiert wird, ohne (!) Die Rekursion direkt zu verwenden:
Die
nonTerminating
Funktion "entpackt" eine Funktion aus ihrem Argument und fügt sie dem ursprünglichen Argument hinzu. Was hier wichtig ist, ist, dass die meisten Typsysteme keine Übergabe von Funktionen an sich selbst zulassen, so dass ein Begriff wief f
"nicht typecheck" ist, da es keinen Typ gibtf
, der den typechecker erfüllt. Einer der Gründe für die Einführung von Typsystemen ist die Deaktivierung der Selbstanwendung (siehe hier ).Das Umschließen von Datentypen wie dem oben eingeführten kann verwendet werden, um diese Straßensperre auf dem Weg zur Inkonsistenz zu umgehen.
Ich möchte hinzufügen, dass nicht terminierende Berechnungen zu Inkonsistenzen bei Logiksystemen führen. Im Fall von Agda und Coq hat der
False
induktive Datentyp keine Konstruktoren, sodass Sie niemals einen Beweisbegriff vom Typ False erstellen können. Aber wenn nicht terminierende Berechnungen erlaubt wären , könnte man dies zum Beispiel so machen (in Coq):Dann
loop 0
würde typecheck gebenloop 0 : False
, also würde es unter Curry-Howard-Korrespondenz bedeuten, dass wir eine falsche Aussage bewiesen haben.Fazit : Die strenge Positivitätsregel für induktive Definitionen verhindert nicht terminierende Berechnungen, die für die Logik katastrophal sind.
quelle