Ich bin relativ spät in meinem Programmierleben auf den Curry-Howard-Isomorphismus gestoßen , und vielleicht trägt dies dazu bei, dass ich davon total fasziniert bin. Dies impliziert, dass es für jedes Programmierkonzept ein genaues Analogon in der formalen Logik gibt und umgekehrt. Hier ist eine "grundlegende" Liste solcher Analogien:
program/definition | proof
type/declaration | proposition
inhabited type | theorem/lemma
function | implication
function argument | hypothesis/antecedent
function result | conclusion/consequent
function application | modus ponens
recursion | induction
identity function | tautology
non-terminating function | absurdity/contradiction
tuple | conjunction (and)
disjoint union | disjunction (or) -- corrected by Antal S-Z
parametric polymorphism | universal quantification
Also zu meiner Frage: Was sind einige der interessanteren / dunkeleren Implikationen dieses Isomorphismus? Ich bin kein Logiker, also bin ich sicher, dass ich mit dieser Liste nur die Oberfläche zerkratzt habe.
Zum Beispiel sind hier einige Programmierbegriffe, für die ich keine markigen Namen in der Logik kenne:
currying | "((a & b) => c) iff (a => (b => c))"
scope | "known theory + hypotheses"
Und hier sind einige logische Konzepte, die ich in Bezug auf die Programmierung nicht ganz festgelegt habe:
primitive type? | axiom
set of valid programs? | theory
Bearbeiten:
Hier sind einige weitere Äquivalenzen aus den Antworten:
function composition | syllogism -- from Apocalisp
continuation-passing | double negation -- from camccann
quelle
goto | jumping to conclusions
Antworten:
Da Sie ausdrücklich nach den interessantesten und dunkelsten gefragt haben:
Sie können CH auf viele interessante Logiken und Logikformulierungen erweitern, um eine wirklich große Vielfalt an Entsprechungen zu erhalten. Hier habe ich versucht, mich auf einige der interessanteren und nicht auf die dunklen zu konzentrieren, sowie auf einige grundlegende, die noch nicht aufgetaucht sind.
EDIT: Eine Referenz, die ich jedem empfehlen würde, der mehr über Erweiterungen von CH erfahren möchte:
"Eine urteilsmäßige Rekonstruktion der Modallogik" http://www.cs.cmu.edu/~fp/papers/mscs00.pdf - dies ist ein großartiger Ausgangspunkt, da er von ersten Prinzipien ausgeht und vieles davon sein soll zugänglich für Nichtlogiker / Sprachtheoretiker. (Ich bin der zweite Autor, also bin ich voreingenommen.)
quelle
Sie trüben die Dinge ein wenig in Bezug auf die Nichtterminierung. Falschheit wird durch unbewohnte Typen dargestellt , die per Definition nicht unendlich sein können, da überhaupt nichts von diesem Typ zu bewerten ist.
Nichtbeendigung stellt Widerspruch dar - eine inkonsistente Logik. Eine inkonsistente Logik wird natürlich können Sie beweisen , alles , einschließlich Falschen, aber.
Ohne Berücksichtigung von Inkonsistenzen entsprechen Typsysteme typischerweise einer intuitionistischen Logik und sind notwendigerweise konstruktivistisch , was bedeutet, dass bestimmte Teile der klassischen Logik nicht direkt oder überhaupt nicht ausgedrückt werden können. Auf der anderen Seite ist dies nützlich, denn wenn ein Typ ein gültiger konstruktiver Beweis ist, ist ein Begriff dieses Typs ein Mittel, um das zu konstruieren, wovon Sie die Existenz bewiesen haben .
Ein Hauptmerkmal des konstruktivistischen Geschmacks ist, dass doppelte Negation nicht gleichbedeutend mit Nicht-Negation ist. In der Tat, Negation selten eine primitive in einem Typ - System, so dass anstelle wir es als was impliziert , Lüge darstellen kann, zB
not P
wirdP -> Falsity
. Doppelte Negation wäre also eine Funktion mit Typ(P -> Falsity) -> Falsity
, die eindeutig nicht gleichbedeutend mit etwas Typischem istP
.Es gibt jedoch eine interessante Wendung! In einer Sprache mit parametrischem Polymorphismus erstrecken sich Typvariablen über alle möglichen Typen, einschließlich unbewohnter, so dass ein vollständig polymorpher Typ, wie er
∀a. a
in gewissem Sinne fast falsch ist. Was ist, wenn wir durch Polymorphismus eine doppelte Fast-Negation schreiben? Wir bekommen einen Typ, der so aussieht :∀a. (P -> a) -> a
. Entspricht das etwas TypischemP
? In der Tat ist es nur auf die Identitätsfunktion anzuwenden.Aber worum geht es? Warum so einen Typ schreiben? Ist es Mittelwert etwas Begriffe in der Programmierung? Nun, Sie können sich das als eine Funktion
P
vorstellen, die irgendwo schon etwas vom Typ hat , und Sie müssen ihr eine Funktion geben, dieP
als Argument dient, wobei das Ganze im Endergebnis-Typ polymorph ist. In gewissem Sinne handelt es sich um eine angehaltene Berechnung , die darauf wartet, dass der Rest bereitgestellt wird. In diesem Sinne können diese angehaltenen Berechnungen zusammengesetzt, herumgereicht, aufgerufen werden, was auch immer. Dies sollte Fans einiger Sprachen wie Scheme oder Ruby bekannt vorkommen - denn es bedeutet, dass die doppelte Negation dem Stil der Weitergabe entsprichtund tatsächlich ist der Typ, den ich oben angegeben habe, genau die Fortsetzungsmonade in Haskell.quelle
P -> Falsity
. Ich verstehe, warum es funktioniert (¬p ≡ p → ⊥), aber ich bekomme die Codeversion nicht.P -> ⊥
sollte genau dann bewohnt sein, wennP
nicht, oder? Aber sollte diese Funktion nicht immer bewohnt sein? Oder eigentlich nie möglich, da Sie eine Instanz von nicht zurückgeben können⊥
? Ich sehe die Konditionalität nicht ganz. Was ist die Intuition hier?P -> Falsity
gleichbedeutend mitP
falsch.f x = x
, die sofort instanziierbar wäreP = ⊥
, aber das war eindeutig nicht generisch genug. Die Idee ist also, dass Sie keinen Körper brauchen , um einen wertlosen Typ zurückzugeben. Aber damit die Funktion definierbar und vollständig ist, brauchen Sie keine Fälle . Wenn SieP
also unbewohnt sind, funktioniert alles? Das ist ein bisschen wackelig, aber ich glaube ich sehe es. Das scheint ziemlich seltsam mit meiner Definition desXor
Typs zu interagieren ... Ich muss darüber nachdenken. Vielen Dank!Ihr Diagramm ist nicht ganz richtig; In vielen Fällen haben Sie Typen mit Begriffen verwechselt.
[1] Die Logik für eine Turing-vollständige Funktionssprache ist inkonsistent. Rekursion hat keine Entsprechung in konsistenten Theorien. In einer inkonsistenten Logik- / Unsound-Proof-Theorie könnte man es eine Regel nennen, die Inkonsistenz / Unsoundness verursacht.
[2] Auch dies ist eine Folge der Vollständigkeit. Dies wäre ein Beweis für ein Antisatz, wenn die Logik konsistent wäre - sie kann also nicht existieren.
[3] Existiert nicht in funktionalen Sprachen, da sie logische Merkmale erster Ordnung eliminieren: Alle Quantifizierungen und Parametrisierungen erfolgen über Formeln. Wenn Sie erste Ordnung Funktionen hätten, wäre es eine Art anders als
*
,* -> *
usw .; die Art der Elemente der Domäne des Diskurses. Zum Beispiel inFather(X,Y) :- Parent(X,Y), Male(X)
,X
undY
Bereich über die Domäne des Diskurses (nennen wir esDom
), undMale :: Dom -> *
.quelle
quelle
Diese Frage gefällt mir sehr gut. Ich weiß nicht viel, aber ich habe ein paar Dinge (unterstützt durch den Wikipedia-Artikel , der einige nette Tabellen und dergleichen selbst enthält):
Ich denke, dass Summentypen / Vereinigungstypen ( z. B.
data Either a b = Left a | Right b
) einer inklusiven Disjunktion entsprechen. Und obwohl ich Curry-Howard nicht sehr gut kenne, denke ich, dass dies dies demonstriert. Betrachten Sie die folgende Funktion:Wenn ich die Dinge richtig verstehe, sagt der Typ ( a ∧ b ) → ( a ★ b ) und die Definition besagt, dass dies wahr ist, wobei ★ entweder inklusive oder exklusiv ist oder je nachdem, was
Either
repräsentiert. Sie haben dieEither
Vertretung exklusiv oder, ⊕; jedoch ( a ∧ b ) ↛ ( a ⊕ b ). Zum Beispiel ⊤ ∧ ⊤ ≡ ≡, aber ⊤ ⊕ ⊥ ≡ ≡ und ⊤ ↛ ⊥. Mit anderen Worten, wenn sowohl a als auch b wahr sind, dann ist die Hypothese wahr, aber die Schlussfolgerung ist falsch, und daher muss diese Implikation falsch sein. Es ist jedoch klar, dass ( a ∧ b ) → ( a ∨ b ), da, wenn sowohl a als auch b wahr sind, mindestens eins wahr ist. Wenn diskriminierte Gewerkschaften eine Form der Disjunktion sind, müssen sie die inklusive Variante sein. Ich denke, dies ist ein Beweis, aber ich fühle mich mehr als frei, mich von dieser Vorstellung abzubringen.In ähnlicher Weise sind Ihre Definitionen für Tautologie und Absurdität als Identitätsfunktion bzw. nicht terminierende Funktionen etwas abweichend. Die wahre Formel wird durch den Einheitentyp dargestellt , der nur ein Element enthält (
data ⊤ = ⊤
häufig geschrieben()
und / oderUnit
in funktionalen Programmiersprachen). Dies ist sinnvoll: Da dieser Typ garantiert bewohnt ist und es nur einen möglichen Einwohner gibt, muss er wahr sein. Die Identitätsfunktion repräsentiert nur die bestimmte Tautologie, die a → a .Ihr Kommentar zu nicht terminierenden Funktionen ist, je nachdem, was Sie genau gemeint haben, eher falsch. Curry-Howard funktioniert auf dem Typsystem, aber die Nichtbeendigung wird dort nicht codiert. Laut Wikipedia , mit Nicht-Beendigung zu tun ist ein Thema, das Hinzufügen es inkonsequent Logik erzeugt ( zB kann ich definieren ,
wrong :: a -> b
durchwrong x = wrong x
, und damit „beweisen“ , dass ein → b für alle a und b ). Wenn Sie das mit „Absurdität“ gemeint haben, dann sind Sie genau richtig. Wenn Sie stattdessen die falsche Aussage gemeint haben, dann möchten Sie stattdessen einen unbewohnten Typ, z. B. etwas, das durch definiert istdata ⊥
- das heißt, ein Datentyp, der nicht konstruiert werden kann. Dies stellt sicher, dass es überhaupt keine Werte hat und daher unbewohnt sein muss, was falsch ist. Ich denke, Sie könnten wahrscheinlich auch verwendena -> b
, denn wenn wir nicht terminierende Funktionen verbieten, dann ist dies auch unbewohnt, aber ich bin nicht 100% sicher.Laut Wikipedia werden Axiome auf zwei verschiedene Arten codiert, je nachdem, wie Sie Curry-Howard interpretieren: entweder in den Kombinatoren oder in den Variablen. Ich denke, die Kombinatoransicht bedeutet, dass die primitiven Funktionen, die wir erhalten, die Dinge codieren, die wir standardmäßig sagen können (ähnlich wie modus ponens ein Axiom ist, weil die Funktionsanwendung primitiv ist). Und ich denke, dass die Variablenansicht tatsächlich dasselbe bedeuten kann - Kombinatoren sind schließlich nur globale Variablen, die bestimmte Funktionen sind. Was primitive Typen betrifft: Wenn ich richtig darüber nachdenke, dann denke ich, dass primitive Typen die Entitäten sind - die primitiven Objekte, über die wir Dinge beweisen wollen.
Nach meiner Logik- und Semantikklasse wird die Tatsache, dass ( a ∧ b ) → c ≡ a → ( b → c ) (und auch b → ( a → c )) zumindest in natürlicher Ableitung als Exportäquivalenzgesetz bezeichnet Beweise. Ich habe damals nicht bemerkt, dass es nur Curry war - ich wünschte, ich hätte es getan, denn das ist cool!
Während wir jetzt eine Möglichkeit haben, eine inklusive Disjunktion darzustellen , haben wir keine Möglichkeit, die exklusive Vielfalt darzustellen. Wir sollten in der Lage sein, die Definition der exklusiven Disjunktion zu verwenden, um sie darzustellen: a ⊕ b ≡ ( a ∨ b ) ∧ ¬ ( a ∧ b ). Ich weiß nicht, wie man Negation schreibt, aber ich weiß, dass ¬ p ≡ p → ⊥ und sowohl Implikation als auch Falschheit einfach sind. Wir sollten daher in der Lage sein, eine ausschließliche Disjunktion darzustellen durch:
Dies definiert
⊥
den leeren Typ ohne Werte, was der Falschheit entspricht.Xor
wird dann definiert, um sowohl ( und )Either
ein a oder ein b ( oder ) als auch eine Funktion ( Implikation ) von (a, b) ( und ) bis zum unteren Typ ( falsch ) zu enthalten.Ich habe jedoch keine Ahnung, was dies bedeutet .( Bearbeiten 1: Jetzt tue ich das, siehe nächster Absatz!)Da es keine Werte vom Typ gibt( Edit 1: Ja, Camccann .)(a,b) -> ⊥
(gibt es?), Kann ich nicht verstehen, was dies in einem Programm bedeuten würde. Kennt jemand eine bessere Möglichkeit, über diese oder eine andere Definition nachzudenken?Edit 1: Dank Camccanns Antwort (insbesondere der Kommentare, die er hinterlassen hat, um mir zu helfen), denke ich, dass ich sehe, was hier los ist. Um einen Wert vom Typ Typ zu
Xor a b
erstellen, müssen Sie zwei Dinge angeben. Erstens ein Zeuge für die Existenz eines Elements von entwedera
oderb
als erstes Argument; das heißt, aLeft a
oder aRight b
. Und zweitens ein Beweis dafür, dass es keine Elemente beider Arten gibt,a
undb
- mit anderen Worten, ein Beweis, der(a,b)
unbewohnt ist - als zweites Argument. Was bedeutet es,(a,b) -> ⊥
wenn dies(a,b)
unbewohnt ist, wenn Sie eine Funktion nur dann schreiben können, wenn sie unbewohnt ist? Das würde bedeuten, dass ein Teil eines Objekts vom Typ ist(a,b)
konnte nicht gebaut werden; mit anderen Worten, dass mindestens einer und möglicherweise beide vona
undb
auch unbewohnt sind! In diesem Fall könnten Sie, wenn wir über Mustervergleich nachdenken, unmöglich einen Mustervergleich für ein solches Tupel durchführen: Angenommen, dasb
ist unbewohnt, was würden wir schreiben, das zum zweiten Teil dieses Tupels passen könnte? Daher können wir keine Musterübereinstimmung damit durchführen, was Ihnen möglicherweise hilft, zu erkennen, warum dies dazu führt, dass es unbewohnt ist. Die einzige Möglichkeit, eine Gesamtfunktion zu haben, die keine Argumente akzeptiert (wie diese muss, da sie(a,b)
unbewohnt ist), besteht darin, dass das Ergebnis auch von unbewohntem Typ ist - wenn wir dies aus einer Musteranpassungsperspektive betrachten, Dies bedeutet, dass es keinen möglichen Körper gibt, obwohl die Funktion keine Fälle hat es könnte beides haben, und so ist alles in Ordnung.Vieles davon ist, dass ich laut nachdenke / (hoffentlich) Dinge im laufenden Betrieb beweise, aber ich hoffe, dass es nützlich ist. Ich kann den Wikipedia-Artikel nur empfehlen . Ich habe es nicht detailliert durchgelesen, aber die Tabellen sind eine wirklich schöne Zusammenfassung und es ist sehr gründlich.
quelle
Either a a
sollte kein Satz sein:Either ⊥ ⊥
ist immer noch unbewohnt. Tom: Wie Camccann sagte, impliziert Konsistenz eine Kündigung. Ein konsistentes Typsystem erlaubt es Ihnen also nicht, sich auszudrückenf :: a -> b
, und der Typ wäre unbewohnt. Ein inkonsistentes Typsystem hätte einen Einwohner für den Typ, der jedoch nicht enden würde. camccann: Gibt es inkonsistente Typsysteme, die nicht vollständig sind und einen Zwischenpunkt in der Hierarchie einnehmen? Oder ist dieser letzte Schritt (Hinzufügen einer allgemeinen Rekursion oder was auch immer) genau gleichbedeutend mit Inkonsistenz?Hier ist eine etwas dunkle, von der ich überrascht bin, dass sie nicht früher angesprochen wurde: "klassische" funktionale reaktive Programmierung entspricht zeitlicher Logik.
Wenn Sie kein Philosoph, Mathematiker oder obsessiver Funktionsprogrammierer sind, wirft dies wahrscheinlich noch einige weitere Fragen auf.
Zunächst einmal: Was ist funktionale reaktive Programmierung? Es ist eine deklarative Art, mit zeitlich variierenden Werten zu arbeiten . Dies ist nützlich, um Dinge wie Benutzeroberflächen zu schreiben, da Eingaben des Benutzers Werte sind, die sich im Laufe der Zeit ändern. "Klassisches" FRP hat zwei grundlegende Datentypen: Ereignisse und Verhaltensweisen.
Ereignisse stellen Werte dar, die nur zu diskreten Zeiten existieren. Tastenanschläge sind ein gutes Beispiel: Sie können sich die Eingaben über die Tastatur zu einem bestimmten Zeitpunkt als Zeichen vorstellen. Jeder Tastendruck ist dann nur ein Paar mit dem Zeichen der Taste und der Zeit, zu der sie gedrückt wurde.
Verhaltensweisen sind Werte, die ständig existieren, sich aber kontinuierlich ändern können. Die Mausposition ist ein gutes Beispiel: Es ist nur ein Verhalten von x, y-Koordinaten. Schließlich hat die Maus immer eine Position, und konzeptionell ändert sich diese Position kontinuierlich, wenn Sie die Maus bewegen. Schließlich ist das Bewegen der Maus eine einzige langwierige Aktion, keine diskreten Schritte.
Und was ist zeitliche Logik? Passenderweise handelt es sich um eine Reihe logischer Regeln für den Umgang mit Aussagen, die im Laufe der Zeit quantifiziert wurden. Im Wesentlichen erweitert es die normale Logik erster Ordnung um zwei Quantifizierer: □ und ◇. Das erste bedeutet "immer": Lesen Sie □ φ als "φ gilt immer". Das zweite ist "irgendwann": ◇ φ bedeutet, dass "φ irgendwann halten wird". Dies ist eine besondere Art von Modallogik . Die folgenden zwei Gesetze beziehen sich auf die Quantifizierer:
Also sind □ und ◇ auf die gleiche Weise wie ∀ und ∃ dual zueinander.
Diese beiden Quantifizierer entsprechen den beiden Typen in FRP. Insbesondere entspricht □ Verhaltensweisen und ◇ Ereignissen. Wenn wir darüber nachdenken, wie diese Typen bewohnt sind, sollte dies Sinn machen: Ein Verhalten wird zu jeder möglichen Zeit bewohnt , während ein Ereignis nur einmal auftritt.
quelle
In Bezug auf die Beziehung zwischen Fortsetzungen und doppelter Negation ist die Art des Anrufs / cc das Peirce-Gesetz http://en.wikipedia.org/wiki/Call-with-current-continuation
CH wird normalerweise als Entsprechung zwischen intuitionistischer Logik und Programmen angegeben. Wenn wir jedoch den Call-with-Current-Continuation-Operator (callCC) hinzufügen (dessen Typ dem Peirce-Gesetz entspricht), erhalten wir eine Entsprechung zwischen klassischer Logik und Programmen mit callCC.
quelle
Obwohl es sich nicht um einen einfachen Isomorphismus handelt, ist diese Diskussion über konstruktives LEM ein sehr interessantes Ergebnis. Insbesondere im Abschlussabschnitt diskutiert Oleg Kiselyov, wie die Verwendung von Monaden zur Eliminierung der doppelten Negation in einer konstruktiven Logik analog zur Unterscheidung von rechnerisch entscheidbaren Sätzen (für die LEM in einem konstruktiven Umfeld gültig ist) von allen Sätzen ist. Die Vorstellung, dass Monaden Recheneffekte erfassen, ist alt, aber diese Instanz des Curry-Howard-Isomorphismus hilft, sie ins rechte Licht zu rücken und herauszufinden, was Doppel-Negation wirklich "bedeutet".
quelle
Mit erstklassiger Unterstützung für Fortsetzungen können Sie $ P \ lor \ neg P $ ausdrücken. Der Trick basiert auf der Tatsache, dass das Nichtaufrufen der Fortsetzung und das Beenden mit einem Ausdruck dem Aufrufen der Fortsetzung mit demselben Ausdruck entspricht.
Eine detailliertere Ansicht finden Sie unter: http://www.cs.cmu.edu/~rwh/courses/logic/www-old/handouts/callcc.pdf
quelle
Eine Sache, die wichtig ist, aber noch nicht untersucht wurde, ist die Beziehung zwischen 2-Fortsetzungen (Fortsetzungen, die 2 Parameter annehmen ) und Sheffer-Schlaganfall . In der klassischen Logik kann der Sheffer-Strich selbst ein vollständiges Logiksystem bilden (plus einige Nicht-Operator-Konzepte). Welche das vertraute bedeutet
and
,or
,not
kann mit oder nur die Sheffer stoke umgesetzt werdennand
.Dies ist eine wichtige Tatsache der Programmtyp-Korrespondenz, da sie dazu auffordert, dass ein einzelner Typkombinator verwendet werden kann, um alle anderen Typen zu bilden.
Die Typensignatur einer 2-Fortsetzung ist
(a,b) -> Void
. Durch diese Implementierung können wir 1-Fortsetzung (normale Fortsetzung) als(a,a)
-> Void, Produkttyp als((a,b)->Void,(a,b)->Void)->Void
, Summentyp als definieren((a,a)->Void,(b,b)->Void)->Void
. Dies gibt uns einen beeindruckenden Eindruck von seiner Ausdruckskraft.Wenn wir weiter graben, werden wir herausfinden, dass das existentielle Diagramm von Piece einer Sprache entspricht, deren einziger Datentyp n-Fortsetzung ist, aber ich habe nicht gesehen, dass vorhandene Sprachen in dieser Form vorliegen. Es könnte also interessant sein, einen zu erfinden, denke ich.
quelle