Gestatten Sie mir zunächst, Ihnen ein Kompliment für Ihr feines pw0n1e-Symbol zu machen.
Diese Frage ist schwierig zu beantworten, vor allem, weil es so viele Varianten von miniKanren und Prolog gibt. miniKanren und Prolog sind wirklich Sprachfamilien, was es schwierig macht, ihre Funktionen oder sogar ihre Verwendung in der Praxis zu vergleichen. Aus diesem Grund nehmen Sie bitte alles, was ich sagen möchte, mit Vorsicht: Wenn ich sage, dass Prolog die Tiefensuche verwendet, beachten Sie, dass viele Prolog-Implementierungen andere Suchstrategien unterstützen und dass alternative Suchstrategien auch im Meta codiert werden können -Dolmetscherebene. Trotzdem haben miniKanren und Prolog unterschiedliche Designphilosophien und machen unterschiedliche Kompromisse.
Prolog ist eine der beiden klassischen Sprachen für die symbolische Programmierung künstlicher Intelligenz (die andere klassische Sprache ist Lisp). Prolog zeichnet sich durch die Implementierung symbolischer regelbasierter Systeme aus, in denen deklaratives Wissen in Logik erster Ordnung codiert ist. Die Sprache ist hinsichtlich Ausdruckskraft und Effizienz für diese Art von Anwendungen optimiert, manchmal auf Kosten der logischen Reinheit. Beispielsweise verwendet Prolog standardmäßig nicht die "Auftrittsprüfung" in der Vereinigung. Aus mathematischer / logischer Sicht ist diese Version der Vereinigung falsch. Die Ereignisprüfung ist jedoch teuer, und in den meisten Fällen ist das Fehlen der Ereignisprüfung kein Problem. Dies ist eine sehr pragmatische Entwurfsentscheidung, ebenso wie Prologs Verwendung der Tiefensuche und die Verwendung des Schnitts (!
), um das Backtracking zu steuern. Ich bin mir sicher, dass diese Entscheidungen auf der Hardware der 1970er Jahre absolut notwendig waren und heute sehr nützlich sind, wenn Sie an großen Problemen arbeiten und große (oft unendliche!) Suchräume bearbeiten.
Prolog unterstützt viele "extralogische" oder "nicht logische" Funktionen, einschließlich Ausschneiden assert
und retract
Projektion von Variablen für die arithmetische Verwendungis
, und so weiter. Viele dieser Funktionen erleichtern es, komplexe Kontrollabläufe auszudrücken und die globale Datenbank von Prolog mit Fakten zu manipulieren. Ein sehr interessantes Merkmal von Prolog ist, dass der Prolog-Code selbst in der globalen Datenbank der Fakten gespeichert ist und zur Laufzeit abgefragt werden kann. Dies macht es trivial, Meta-Interpreter zu schreiben, die das Verhalten des zu interpretierenden Prolog-Codes ändern. Beispielsweise ist es möglich, die Breitensuche in Prolog mit einem Meta-Interpreter zu codieren, der die Suchreihenfolge ändert. Dies ist eine äußerst leistungsfähige Technik, die außerhalb der Prolog-Welt nicht bekannt ist. 'The Art of Prolog' beschreibt diese Technik im Detail.
Es wurden enorme Anstrengungen unternommen, um die Prolog-Implementierungen zu verbessern, von denen die meisten auf der Warren Abstract Machine (WAM) basieren. Das WAM verwendet ein Nebenwirkungsmodell, bei dem logischen Variablen Werte destruktiv zugewiesen werden, wobei diese Nebeneffekte beim Zurückverfolgen rückgängig gemacht werden. Viele Funktionen können zu Prolog hinzugefügt werden, indem die Anweisungen des WAM erweitert werden. Ein Nachteil dieses Ansatzes besteht darin, dass Prolog-Implementierungspapiere ohne ein solides Verständnis des WAM schwer zu lesen sein können. Auf der anderen Seite haben Prolog-Implementierer ein gemeinsames Modell für die Erörterung von Implementierungsproblemen. Parallel zum Prolog wurde viel geforscht, was in den 1990er Jahren in Andorra Prolog gipfelte. Zumindest einige dieser Ideen leben in Ciao Prolog weiter. (Ciao Prolog steckt voller interessanter Ideen, von denen viele weit über den Prolog-Standard hinausgehen.)
Prolog hat eine schöne vereinheitlichungsbasierte Syntax im "Pattern Matching" -Stil, die zu sehr prägnanten Programmen führt. Prologen lieben ihre Syntax, genau wie Lisper ihre S-Ausdrücke lieben. Prolog hat auch eine große Bibliothek von Standardprädikaten. Aufgrund all der Technik, mit der das WAM schnell gemacht wurde, gibt es sehr leistungsfähige und ausgereifte Prolog-Implementierungen. Infolgedessen wurden viele große wissensbasierte Systeme vollständig in Prolog geschrieben.
miniKanren wurde als minimale logische Programmiersprache mit einer kleinen, leicht verständlichen und leicht hackbaren Implementierung entwickelt. miniKanren war ursprünglich in Scheme eingebettet und wurde in den letzten zehn Jahren auf Dutzende anderer Hostsprachen portiert. Die beliebteste miniKanren-Implementierung ist 'core.logic' in Clojure, das jetzt viele Prolog-ähnliche Erweiterungen und eine Reihe von Optimierungen enthält. Kürzlich wurde der Kern der miniKanren-Implementierung noch weiter vereinfacht, was zu einem winzigen "Mikrokernel" namens "microKanren" führte. Auf diesem microKanren-Kern kann dann miniKanren implementiert werden. Das Portieren von microKanren oder miniKanren in eine neue Hostsprache ist zu einer Standardübung für Programmierer geworden, die miniKanren lernen. Als Ergebnis,
Die Standardimplementierungen von miniKanren und microKanren enthalten keine Mutation oder andere Nebenwirkungen, mit einer einzigen Ausnahme: Einige Versionen von miniKanren verwenden die Zeigergleichheit zum Vergleich von Logikvariablen. Ich halte dies für einen "harmlosen Effekt", obwohl viele Implementierungen selbst diesen Effekt vermeiden, indem sie einen Zähler durch die Implementierung führen. Es gibt auch keine globale Faktendatenbank. Die Implementierungsphilosophie von miniKanren ist von der funktionalen Programmierung inspiriert: Mutationen und Effekte sollten vermieden werden, und alle Sprachkonstrukte sollten den lexikalischen Umfang berücksichtigen. Wenn Sie sich die Implementierung genau ansehen, können Sie sogar ein paar Monaden entdecken. Die Suchimplementierung basiert auf dem Kombinieren und Bearbeiten von Lazy Streams, wiederum ohne Mutation. Diese Implementierungsentscheidungen führen zu sehr unterschiedlichen Kompromissen als bei Prolog. In Prolog ist die variable Suche eine konstante Zeit, aber das Zurückverfolgen erfordert das Rückgängigmachen von Nebenwirkungen. In miniKanren ist die variable Suche teurer, aber das Backtracking ist "kostenlos". Tatsächlich gibt es in miniKanren kein Backtracking, da die Streams so behandelt werden.
Ein interessanter Aspekt der miniKanren-Implementierung ist, dass der Code von Natur aus threadsicher und - zumindest theoretisch - trivial parallelisierbar ist. Natürlich ist es nicht trivial, den Code zu parallelisieren, ohne ihn langsamer zu machen, da jedem Thread oder Prozess genügend Arbeit gegeben werden muss, um den Aufwand für die Parallelisierung auszugleichen. Dies ist jedoch ein Bereich der Implementierung von miniKanren, von dem ich hoffe, dass er mehr Aufmerksamkeit und Experimente erhält.
miniKanren verwendet die Ereignisprüfung zur Vereinheitlichung und verwendet anstelle der Tiefensuche eine vollständige Verschachtelungssuche. Die Interleaving-Suche benötigt mehr Speicher als die Tiefensuche, kann jedoch in einigen Fällen Antworten finden, in denen die Tiefensuche für immer divergiert / wiederholt wird. miniKanren tut unterstützen ein paar extra-logische Operatoren --- conda
, condu
und project
zum Beispiel. conda
und condu
kann verwendet werden, um den Schnitt von Prolog zu simulieren, und project
kann verwendet werden, um den Wert zu erhalten, der einer logischen Variablen zugeordnet ist.
Die Anwesenheit von conda
, condu
, undproject
--- und die Möglichkeit, die Suchstrategie einfach zu ändern --- ermöglicht es Programmierern, miniKanren als eingebettete Prolog-ähnliche Sprache zu verwenden. Dies gilt insbesondere für Benutzer von Clojures 'core.logic', das viele Prolog-ähnliche Erweiterungen enthält. Diese "pragmatische" Verwendung von miniKanren scheint den größten Teil der Verwendung von miniKanren in der Industrie zu erklären. Programmierer, die einer vorhandenen Anwendung in Clojure, Python oder JavaScript ein wissensbasiertes Argumentationssystem hinzufügen möchten, sind im Allgemeinen nicht daran interessiert, ihre gesamte Anwendung in Prolog neu zu schreiben. Das Einbetten einer kleinen Logikprogrammiersprache in Clojure oder Python ist viel ansprechender. Eine eingebettete Prolog-Implementierung würde für diesen Zweck vermutlich genauso gut funktionieren.
Neben der Verwendung von miniKanren als pragmatische Programmiersprache für eingebettete Logik, die Prolog ähnelt, wird miniKanren für die Erforschung der "relationalen" Programmierung verwendet. Das heißt, beim Schreiben von Programmen, die sich eher als mathematische Beziehungen als als mathematische Funktionen verhalten. In Schema kann die append
Funktion beispielsweise zwei Listen anhängen und eine neue Liste zurückgeben: Der Funktionsaufruf (append '(a b c) '(d e))
gibt die Liste zurück (a b c d e)
. Wir können jedoch auch append
als Drei-Stellen-Beziehung und nicht als Zwei-Argument-Funktion behandeln. Der Aufruf (appendo '(a b c) '(d e) Z)
würde dann die logische Variable Z
der Liste zuordnen (a b c d e)
. Natürlich wird es interessanter, wenn wir logische Variablen an anderen Positionen platzieren. Der Ruf (appendo X '(d e) '(a b c d e))
verbindet X
mit (a b c)
, während der Anruf(appendo X Y '(a b c d e))
assoziiert X
und Y
mit Listenpaaren, die, wenn sie angehängt werden, gleich sind (a b c d e)
. Zum Beispiel sind X
= (a b)
und Y
= (c d e)
ein solches Wertepaar. Wir können auch schreiben (appendo X Y Z)
, die unendlich viele Tripel von Listen produzieren X
, Y
und Z
so , dass das Anhängen X
zu Y
erzeugt Z
.
Diese relationale Version von append
kann leicht in Prolog ausgedrückt werden und wird in der Tat in vielen Prolog-Tutorials gezeigt. In der Praxis verwenden komplexere Prolog-Programme in der Regel mindestens einige zusätzliche logische Merkmale, z. B. "Ausschneiden", die die Fähigkeit beeinträchtigen, das resultierende Programm als Beziehung zu behandeln. Im Gegensatz dazu wurde miniKanren explizit entwickelt, um diesen Stil der relationalen Programmierung zu unterstützen. Neuere Versionen von miniKanren Unterstützung haben für symbolische Einschränkung Lösung ( symbolo
, numbero
,absento
, Ungleichheitsbeschränkungen, nominale Logikprogrammierung), um das Schreiben nicht trivialer Programme als Relationen zu erleichtern. In der Praxis verwende ich niemals eine der extralogischen Funktionen von miniKanren und schreibe alle meine miniKanren-Programme als Relationen. Die interessantesten relationalen Programme sind die relationalen Interpreter für eine Teilmenge des Schemas. Diese Dolmetscher verfügen über viele interessante Fähigkeiten, z. B. das Generieren einer Million Schema-Programme, die anhand der Liste ausgewertet werden (I love you)
, oder das triviale Generieren von Quines (Programme, die sich selbst bewerten).
miniKanren macht eine Reihe von Kompromissen, um diesen relationalen Programmierstil zu ermöglichen, die sich stark von den Kompromissen unterscheiden, die Prolog macht. Im Laufe der Zeit hat miniKanren mehr symbolische Einschränkungen hinzugefügt und sich zu einer symbolisch orientierten Programmiersprache für Constraint Logic entwickelt. In vielen Fällen machen es diese symbolischen Einschränkungen praktisch, die Verwendung von extralogischen Operatoren wie condu
und zu vermeiden project
. In anderen Fällen reichen diese symbolischen Einschränkungen nicht aus. Eine bessere Unterstützung für symbolische Einschränkungen ist ein aktiver Bereich der miniKanren-Forschung, zusammen mit der umfassenderen Frage, wie größere und komplexere Programme als Beziehungen geschrieben werden können.
Kurz gesagt, sowohl miniKanren als auch Prolog haben interessante Funktionen, Implementierungen und Verwendungen, und ich denke, es lohnt sich, die Ideen aus beiden Sprachen zu lernen. Es gibt auch andere sehr interessante Logikprogrammiersprachen wie Mercury, Curry und Gödel, von denen jede ihre eigene Sicht auf die Logikprogrammierung hat.
Ich werde mit ein paar miniKanren-Ressourcen enden:
Die Hauptwebsite von miniKanren:
http://minikanren.org/
Ein Interview über relationale Programmierung und miniKanren, einschließlich eines Vergleichs mit Prolog:
http://www.infoq.com/interviews/byrd-relational-programming-minikanren
Prost,
--Wille