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.
data Good : Set where
good : Good → Good → Good
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:
data Strange : Set where
strange : ((Bool → Strange) → (ℕ → Strange)) → Strange
^^ ^
this Strange is ...this arrow
to the left of...
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):
Nicht streng positive Deklarationen werden abgelehnt, weil man damit eine nicht terminierende Funktion schreiben kann. Informationen zum Schreiben einer Schleifendefinition mit dem Datentyp Bad von oben finden Sie unter BadInHaskell .
Hier ist ein analoges Beispiel in Ocaml, das zeigt, wie rekursives Verhalten implementiert wird, ohne (!) Die Rekursion direkt zu verwenden:
type boxed_fun =
| Box of (boxed_fun -> boxed_fun)
(* (!) in Ocaml the 'let' construct does not permit recursion;
one have to use the 'let rec' construct to bring
the name of the function under definition into scope
*)
let nonTerminating (bf:boxed_fun) : boxed_fun =
match bf with
Box f -> f bf
let loop = nonTerminating (Box nonTerminating)
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 wie f f
"nicht typecheck" ist, da es keinen Typ gibt f
, 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):
Fixpoint loop (n : nat) : False = loop n
Dann loop 0
würde typecheck geben loop 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.
A
Stapels verursachen und ihn schließlich explodieren lassen (in stapelbasierten Sprachen).