Per prima cosa, permettimi di farti i complimenti per la tua bella icona pw0n1e.
Questa è una domanda difficile a cui rispondere, soprattutto perché ci sono così tante varianti sia di miniKanren che di Prolog. miniKanren e Prolog sono in realtà famiglie di linguaggi, il che rende difficile confrontare le loro caratteristiche o anche il modo in cui vengono utilizzate nella pratica. Per questo motivo, prendi con cautela tutto ciò che sto per dire: se dico che Prolog utilizza la ricerca approfondita, tieni presente che molte implementazioni di Prolog supportano altre strategie di ricerca e che le strategie di ricerca alternative possono anche essere codificate nel meta -livello interprete. Tuttavia, miniKanren e Prolog hanno filosofie di progettazione diverse e fanno diversi compromessi.
Il prologo è uno dei due linguaggi classici per la programmazione dell'intelligenza artificiale simbolica (l'altro linguaggio classico è Lisp). Prolog eccelle nell'implementazione di sistemi simbolici basati su regole in cui la conoscenza dichiarativa è codificata nella logica del primo ordine. Il linguaggio è ottimizzato per espressività ed efficienza per questi tipi di applicazioni, a volte a scapito della purezza logica. Ad esempio, per impostazione predefinita Prolog non utilizza il "controllo di verifica" nell'unificazione. Da un punto di vista matematico / logico, questa versione dell'unificazione non è corretta. Tuttavia, il controllo in corso è costoso e nella maggior parte dei casi la mancanza del controllo in corso non è un problema. Questa è una decisione di progettazione molto pragmatica, così come l'uso di Prolog della ricerca in profondità e l'uso del taglio (!
) per controllare il backtracking. Sono sicuro che queste decisioni erano assolutamente necessarie quando si eseguiva l'hardware degli anni '70, e oggi sono molto utili quando si lavora su grandi problemi e quando si ha a che fare con spazi di ricerca enormi (spesso infiniti!).
Prolog supporta molte funzionalità "extra-logiche" o "non logiche", incluso il taglio assert
e la retract
proiezione di variabili per l'uso aritmeticois
, e così via. Molte di queste caratteristiche rendono più facile esprimere un flusso di controllo complesso e manipolare il database globale dei fatti di Prolog. Una caratteristica molto interessante di Prolog è che il codice Prolog è esso stesso memorizzato nel database globale dei fatti e può essere interrogato in fase di esecuzione. Questo rende banale scrivere meta-interpreti che modificano il comportamento del codice Prolog sotto interpretazione. Ad esempio, è possibile codificare la ricerca in ampiezza in Prolog utilizzando un meta-interprete che modifica l'ordine di ricerca. Questa è una tecnica estremamente potente che non è molto conosciuta al di fuori del mondo Prolog. "The Art of Prolog" descrive questa tecnica in dettaglio.
Sono stati compiuti sforzi enormi per migliorare le implementazioni di Prolog, la maggior parte delle quali sono basate sulla Warren Abstract Machine (WAM). Il WAM utilizza un modello con effetti collaterali in cui i valori vengono assegnati in modo distruttivo alle variabili logiche, con questi effetti collaterali che vengono annullati durante il backtracking. Molte funzionalità possono essere aggiunte a Prolog estendendo le istruzioni del WAM. Uno svantaggio di questo approccio è che i documenti di implementazione di Prolog possono essere difficili da leggere senza una solida conoscenza del WAM. D'altra parte, l'implementatore di Prolog ha un modello comune per discutere i problemi di implementazione. Parallelamente a Prolog sono state svolte molte ricerche, culminate in Andorra Prolog negli anni '90. Almeno alcune di queste idee vivono in Ciao Prolog. (Ciao Prolog è pieno di idee interessanti, molte delle quali vanno ben oltre lo standard Prolog.)
Prolog ha una bellissima sintassi in stile "pattern-matching" basata sull'unificazione che si traduce in programmi molto succinti. I prologi amano la loro sintassi, proprio come i Lispers amano le loro espressioni s. Prolog dispone anche di un'ampia libreria di predicati standard. A causa di tutta l'ingegneria necessaria per rendere veloce il WAM, ci sono implementazioni Prolog molto capaci e mature. Di conseguenza, molti grandi sistemi basati sulla conoscenza sono stati scritti interamente in Prolog.
miniKanren è stato progettato come un linguaggio di programmazione logico minimale, con un'implementazione piccola, facilmente comprensibile e facilmente hackerabile. miniKanren era originariamente incorporato in Scheme ed è stato portato in dozzine di altre lingue ospitanti negli ultimi dieci anni. L'implementazione di miniKanren più popolare è "core.logic" in Clojure, che ora ha molte estensioni simili a Prolog e una serie di ottimizzazioni. Recentemente il nucleo dell'implementazione di miniKanren è stato ulteriormente semplificato, risultando in un minuscolo "micro kernel" chiamato "microKanren". miniKanren può quindi essere implementato su questo core microKanren. Il porting di microKanren o miniKanren in una nuova lingua host è diventato un esercizio standard per i programmatori che imparano miniKanren. Di conseguenza,
Le implementazioni standard di miniKanren e microKanren non contengono mutazione o altri effetti collaterali, con una sola eccezione: alcune versioni di miniKanren utilizzano l'uguaglianza dei puntatori per confrontare le variabili logiche. Lo considero un "effetto benefico", sebbene molte implementazioni evitino anche questo effetto passando un contatore attraverso l'implementazione. Inoltre, non esiste un database dei fatti globale. La filosofia di implementazione di miniKanren è ispirata dalla programmazione funzionale: la mutazione e gli effetti dovrebbero essere evitati e tutti i costrutti del linguaggio dovrebbero rispettare l'ambito lessicale. Se osservi attentamente l'implementazione potresti persino individuare un paio di monadi. L'implementazione della ricerca si basa sulla combinazione e la manipolazione di flussi pigri, ancora una volta senza utilizzare la mutazione. Queste scelte di implementazione portano a compromessi molto diversi rispetto a Prolog. In Prolog, la ricerca delle variabili è un tempo costante, ma il backtracking richiede l'annullamento degli effetti collaterali. In miniKanren la ricerca delle variabili è più costosa, ma il backtracking è "gratuito". In effetti, non c'è backtracking in miniKanren, a causa di come vengono gestiti i flussi.
Un aspetto interessante dell'implementazione di miniKanren è che il codice è intrinsecamente thread-safe e --- almeno in teoria --- banalmente parallelizzabile. Ovviamente, parallelizzare il codice senza renderlo più lento non è banale, dato che a ogni thread o processo deve essere dato abbastanza lavoro per compensare il sovraccarico della parallelizzazione. Tuttavia, questa è un'area dell'implementazione di miniKanren che spero riceverà più attenzione e sperimentazione.
miniKanren utilizza il controllo degli eventi per l'unificazione e utilizza una ricerca interleaving completa invece di una ricerca approfondita. La ricerca interleaving utilizza più memoria rispetto alla ricerca in profondità, ma può trovare risposte in alcuni casi in cui la ricerca in profondità diverge / continua per sempre. miniKanren fa sostenere alcuni operatori extra-logici --- conda
, condu
e project
, per esempio. conda
e condu
può essere utilizzato per simulare il taglio di Prolog e project
può essere utilizzato per ottenere il valore associato a una variabile logica.
La presenza di conda
, condu
eproject
--- e la capacità di modificare facilmente la strategia di ricerca --- consente ai programmatori di utilizzare miniKanren come linguaggio incorporato simile a Prolog. Ciò è particolarmente vero per gli utenti di "core.logic" di Clojure, che include molte estensioni simili a Prolog. Questo uso "pragmatico" di miniKanren sembra spiegare la maggior parte dell'uso di miniKanren nell'industria. I programmatori che desiderano aggiungere un sistema di ragionamento basato sulla conoscenza a un'applicazione esistente scritta in Clojure o Python o JavaScript non sono generalmente interessati a riscrivere l'intera applicazione in Prolog. Incorporare un piccolo linguaggio di programmazione logica in Clojure o Python è molto più attraente. Un'implementazione Prolog incorporata funzionerebbe altrettanto bene per questo scopo, presumibilmente.
Oltre all'uso di miniKanren come un pragmatico linguaggio di programmazione logica incorporata simile nello spirito a Prolog, miniKanren viene utilizzato per la ricerca nella programmazione "relazionale". Cioè, nella scrittura di programmi che si comportano come relazioni matematiche piuttosto che come funzioni matematiche. Ad esempio, in Scheme la append
funzione può aggiungere due elenchi, restituendo un nuovo elenco: la chiamata alla funzione (append '(a b c) '(d e))
restituisce l'elenco (a b c d e)
. Tuttavia, possiamo anche trattare append
come una relazione a tre posti piuttosto che come una funzione a due argomenti. La chiamata (appendo '(a b c) '(d e) Z)
associa quindi la variabile logica Z
all'elenco (a b c d e)
. Ovviamente le cose diventano più interessanti quando posizioniamo le variabili logiche in altre posizioni. La chiamata si (appendo X '(d e) '(a b c d e))
associa X
a (a b c)
, mentre la chiamata(appendo X Y '(a b c d e))
associati X
e Y
con coppie di elenchi che, se aggiunti, sono uguali a (a b c d e)
. Ad esempio X
= (a b)
e Y
= (c d e)
sono una di queste coppie di valori. Possiamo anche scrivere (appendo X Y Z)
, che produrrà un numero infinito di triple di liste X
, Y
e Z
in modo tale che aggiungendo X
alla Y
produce Z
.
Questa versione relazionale di append
può essere facilmente espressa in Prolog, e in effetti è mostrata in molti tutorial di Prolog. In pratica, i programmi Prolog più complessi tendono a utilizzare almeno alcune caratteristiche extra logiche, come il taglio, che inibiscono la capacità di trattare il programma risultante come una relazione. Al contrario, miniKanren è esplicitamente progettato per supportare questo stile di programmazione relazionale. Più recenti versioni di miniKanren hanno il supporto per risolvere simbolica vincolo ( symbolo
, numbero
,absento
, vincoli di disuguaglianza, programmazione logica nominale) per rendere più facile scrivere programmi non banali come relazioni. In pratica non uso mai nessuna delle caratteristiche extra logiche di miniKanren, e scrivo tutti i miei programmi miniKanren come relazioni. I programmi relazionali più interessanti sono gli interpreti relazionali per un sottoinsieme di Scheme. Questi interpreti hanno molte capacità interessanti, come la generazione di un milione di programmi Scheme che valutano l'elenco (I love you)
o la generazione banale di quines (programmi che valutano a se stessi).
miniKanren fa una serie di compromessi per abilitare questo stile di programmazione relazionale, che sono molto diversi dai compromessi che Prolog fa. Nel corso del tempo miniKanren ha aggiunto ulteriori vincoli simbolici, diventando davvero un linguaggio di programmazione con logica vincolante orientato simbolicamente. In molti casi questi vincoli simbolici rendono pratico evitare l'uso di operatori extra logici come condu
e project
. In altri casi, questi vincoli simbolici non sono sufficienti. Un supporto migliore per i vincoli simbolici è un'area attiva della ricerca miniKanren, insieme alla domanda più ampia di come scrivere programmi più grandi e complessi come relazioni.
In breve, sia miniKanren che Prolog hanno caratteristiche, implementazioni e usi interessanti e penso che valga la pena imparare le idee da entrambe le lingue. Ci sono anche altri linguaggi di programmazione logica molto interessanti, come Mercury, Curry e Gödel, ognuno dei quali ha il proprio approccio alla programmazione logica.
Concluderò con alcune risorse miniKanren:
Il sito web principale di miniKanren:
http://minikanren.org/
Un'intervista che ho rilasciato sulla programmazione relazionale e miniKanren, incluso un confronto con Prolog:
http://www.infoq.com/interviews/byrd-relational-programming-minikanren
Saluti,
--Volere