miniKanren language basics
; motivation ;; 2 + 3 = 5 ;; (+ 2 3) => 5 ; functional viewpoint ;; (+o 2 3 5) ; relational viewpoint ;; ; success ;; (+o 2 3 6) ;; ; failure ;; (+o 2 3 Z) ; Z <- logic variable Z = 5 ;; (+o X 3 5) ; X <- logic variable X = 2 ;; (+o X Y 5) ; x = 0, y = 5; ... x = 2, y = 3; ... ;; (+o X Y Z) ;; (FOO 2 3 5) ; Core operators ; == (unification) (== 5 5) (== 5 6) (== x 5) (== x y) (== (list 2 3) (list x y)) (fresh () (== x y) (== y 5)) (fresh () (== x y) (== y 5) (== x 6)) (fresh () (== y 5) (== x y)) (conde ((== x 5)) ((== x 6)))
appendo
;; code goes here! (define appendo (lambda (l1 l2 out) (conde ((fresh (first rest result) (== (cons first result) out) (== (cons first rest) l1) (appendo rest l2 result))) ((== '() l1) (== l2 out))))) (appendo '(1 2 3) '(4 5) Z) (appendo '(1 2 3) Y '(1 2 3 4 5)) (appendo X Y '(1 2 3 4 5)) (appendo X Y Z)
type inferencer
;; code goes here!
small relational interpreter
;; code goes here!
larger relational interpreter
;; relational Scheme interpreter with pattern matching ;; and primitives in initial environment ;; The Scheme interpreter: (define empty-env '()) (define (lookupo x env t) (fresh (y b rest) (== `((,y . ,b) . ,rest) env) (conde ((== x y) (conde ((== `(val . ,t) b)) ((fresh (lam-expr) (== `(rec . ,lam-expr) b) (== `(closure ,lam-expr ,env) t))))) ((=/= x y) (lookupo x rest t))))) (define (not-in-envo x env) (conde ((== empty-env env)) ((fresh (y b rest) (== `((,y . ,b) . ,rest) env) (=/= y x) (not-in-envo x rest))))) (define (eval-listo expr env val) (conde ((== '() expr) (== '() val)) ((fresh (a d v-a v-d) (== `(,a . ,d) expr) (== `(,v-a . ,v-d) val) (eval-expo a env v-a) (eval-listo d env v-d))))) ;; need to make sure lambdas are well formed. ;; grammar constraints would be useful here!!! (define (list-of-symbolso los) (conde ((== '() los)) ((fresh (a d) (== `(,a . ,d) los) (symbolo a) (list-of-symbolso d))))) (define (evalo expr val) (eval-expo expr initial-env val)) (define (eval-expo expr env val) (conde ((== `(quote ,val) expr) (absento 'closure val) (absento 'prim val) (not-in-envo 'quote env)) ((numbero expr) (== expr val)) ((symbolo expr) (lookupo expr env val)) ((fresh (x body) (== `(lambda ,x ,body) expr) (== `(closure (lambda ,x ,body) ,env) val) (conde ;; Variadic ((symbolo x)) ;; Multi-argument ((list-of-symbolso x))) (not-in-envo 'lambda env))) ((fresh (rator x rands body env^ a* res) (== `(,rator . ,rands) expr) ;; variadic (symbolo x) (== `((,x . (val . ,a*)) . ,env^) res) (eval-expo rator env `(closure (lambda ,x ,body) ,env^)) (eval-expo body res val) (eval-listo rands env a*))) ((fresh (rator x* rands body env^ a* res) (== `(,rator . ,rands) expr) ;; Multi-argument (eval-expo rator env `(closure (lambda ,x* ,body) ,env^)) (eval-listo rands env a*) (ext-env*o x* a* env^ res) (eval-expo body res val))) ((fresh (rator x* rands a* prim-id) (== `(,rator . ,rands) expr) (eval-expo rator env `(prim . ,prim-id)) (eval-primo prim-id a* val) (eval-listo rands env a*))) ((handle-matcho expr env val)) ((fresh (p-name x body letrec-body) ;; single-function variadic letrec version (== `(letrec ((,p-name (lambda ,x ,body))) ,letrec-body) expr) (conde ; Variadic ((symbolo x)) ; Multiple argument ((list-of-symbolso x))) (not-in-envo 'letrec env) (eval-expo letrec-body `((,p-name . (rec . (lambda ,x ,body))) . ,env) val))) ((prim-expo expr env val)) )) (define (ext-env*o x* a* env out) (conde ((== '() x*) (== '() a*) (== env out)) ((fresh (x a dx* da* env2) (== `(,x . ,dx*) x*) (== `(,a . ,da*) a*) (== `((,x . (val . ,a)) . ,env) env2) (symbolo x) (ext-env*o dx* da* env2 out))))) (define (eval-primo prim-id a* val) (conde ((== prim-id 'cons) (fresh (a d) (== `(,a ,d) a*) (== `(,a . ,d) val))) ((== prim-id 'car) (fresh (d) (== `((,val . ,d)) a*) (=/= 'closure val))) ((== prim-id 'cdr) (fresh (a) (== `((,a . ,val)) a*) (=/= 'closure a))) ((== prim-id 'not) (fresh (b) (== `(,b) a*) (conde ((=/= #f b) (== #f val)) ((== #f b) (== #t val))))) ((== prim-id 'equal?) (fresh (v1 v2) (== `(,v1 ,v2) a*) (conde ((== v1 v2) (== #t val)) ((=/= v1 v2) (== #f val))))) ((== prim-id 'symbol?) (fresh (v) (== `(,v) a*) (conde ((symbolo v) (== #t val)) ((numbero v) (== #f val)) ((fresh (a d) (== `(,a . ,d) v) (== #f val)))))) ((== prim-id 'null?) (fresh (v) (== `(,v) a*) (conde ((== '() v) (== #t val)) ((=/= '() v) (== #f val))))))) (define (prim-expo expr env val) (conde ((boolean-primo expr env val)) ((and-primo expr env val)) ((or-primo expr env val)) ((if-primo expr env val)))) (define (boolean-primo expr env val) (conde ((== #t expr) (== #t val)) ((== #f expr) (== #f val)))) (define (and-primo expr env val) (fresh (e*) (== `(and . ,e*) expr) (not-in-envo 'and env) (ando e* env val))) (define (ando e* env val) (conde ((== '() e*) (== #t val)) ((fresh (e) (== `(,e) e*) (eval-expo e env val))) ((fresh (e1 e2 e-rest v) (== `(,e1 ,e2 . ,e-rest) e*) (conde ((== #f v) (== #f val) (eval-expo e1 env v)) ((=/= #f v) (eval-expo e1 env v) (ando `(,e2 . ,e-rest) env val))))))) (define (or-primo expr env val) (fresh (e*) (== `(or . ,e*) expr) (not-in-envo 'or env) (oro e* env val))) (define (oro e* env val) (conde ((== '() e*) (== #f val)) ((fresh (e) (== `(,e) e*) (eval-expo e env val))) ((fresh (e1 e2 e-rest v) (== `(,e1 ,e2 . ,e-rest) e*) (conde ((=/= #f v) (== v val) (eval-expo e1 env v)) ((== #f v) (eval-expo e1 env v) (oro `(,e2 . ,e-rest) env val))))))) (define (if-primo expr env val) (fresh (e1 e2 e3 t) (== `(if ,e1 ,e2 ,e3) expr) (not-in-envo 'if env) (eval-expo e1 env t) (conde ((=/= #f t) (eval-expo e2 env val)) ((== #f t) (eval-expo e3 env val))))) (define (handle-matcho expr env val) (fresh (against-expr mval clause clauses) (== `(match ,against-expr ,clause . ,clauses) expr) (not-in-envo 'match env) (eval-expo against-expr env mval) (match-clauses mval `(,clause . ,clauses) env val))) (define (not-symbolo t) (conde ((== #f t)) ((== #t t)) ((numbero t)) ((fresh (a d) (== `(,a . ,d) t))))) (define (not-numbero t) (conde ((== #f t)) ((== #t t)) ((symbolo t)) ((fresh (a d) (== `(,a . ,d) t))))) (define (self-eval-literalo t) (conde ((numbero t)) ((booleano t)))) (define (literalo t) (conde ((numbero t)) ((symbolo t) (=/= 'closure t)) ((booleano t)) ((== '() t)))) (define (booleano t) (conde ((== #f t)) ((== #t t)))) (define (regular-env-appendo env1 env2 env-out) (conde ((== empty-env env1) (== env2 env-out)) ((fresh (y v rest res) (== `((,y . (val . ,v)) . ,rest) env1) (== `((,y . (val . ,v)) . ,res) env-out) (regular-env-appendo rest env2 res))))) ;; Pattern-matching extension: (define (match-clauses mval clauses env val) (fresh (top-p result-expr d penv) (== `((,top-p ,result-expr) . ,d) clauses) (conde ((fresh (env^) (top-p-match top-p mval '() penv) (regular-env-appendo penv env env^) (eval-expo result-expr env^ val))) ((top-p-no-match top-p mval '() penv) (match-clauses mval d env val))))) (define (top-p-match top-p mval penv penv-out) (conde ((self-eval-literalo top-p) (== top-p mval) (== penv penv-out)) ((p-match top-p mval penv penv-out)) ((fresh (quasi-p) (== (list 'quasiquote quasi-p) top-p) (quasi-p-match quasi-p mval penv penv-out))))) (define (top-p-no-match top-p mval penv penv-out) (conde ((self-eval-literalo top-p) (=/= top-p mval) (== penv penv-out)) ((p-no-match top-p mval penv penv-out)) ((fresh (quasi-p) (== (list 'quasiquote quasi-p) top-p) (quasi-p-no-match quasi-p mval penv penv-out))))) (define (var-p-match var mval penv penv-out) (fresh (val) (symbolo var) (=/= 'closure mval) (conde ((== mval val) (== penv penv-out) (lookupo var penv val)) ((== `((,var . (val . ,mval)) . ,penv) penv-out) (not-in-envo var penv))))) (define (var-p-no-match var mval penv penv-out) (fresh (val) (symbolo var) (=/= mval val) (== penv penv-out) (lookupo var penv val))) (define (p-match p mval penv penv-out) (conde ((var-p-match p mval penv penv-out)) ((fresh (var pred val) (== `(? ,pred ,var) p) (conde ((== 'symbol? pred) (symbolo mval)) ((== 'number? pred) (numbero mval))) (var-p-match var mval penv penv-out))))) (define (p-no-match p mval penv penv-out) (conde ((var-p-no-match p mval penv penv-out)) ((fresh (var pred val) (== `(? ,pred ,var) p) (== penv penv-out) (symbolo var) (conde ((== 'symbol? pred) (conde ((not-symbolo mval)) ((symbolo mval) (var-p-no-match var mval penv penv-out)))) ((== 'number? pred) (conde ((not-numbero mval)) ((numbero mval) (var-p-no-match var mval penv penv-out))))))))) (define (quasi-p-match quasi-p mval penv penv-out) (conde ((== quasi-p mval) (== penv penv-out) (literalo quasi-p)) ((fresh (p) (== (list 'unquote p) quasi-p) (p-match p mval penv penv-out))) ((fresh (a d v1 v2 penv^) (== `(,a . ,d) quasi-p) (== `(,v1 . ,v2) mval) (=/= 'unquote a) (quasi-p-match a v1 penv penv^) (quasi-p-match d v2 penv^ penv-out))))) (define (quasi-p-no-match quasi-p mval penv penv-out) (conde ((=/= quasi-p mval) (== penv penv-out) (literalo quasi-p)) ((fresh (p) (== (list 'unquote p) quasi-p) (=/= 'closure mval) (p-no-match p mval penv penv-out))) ((fresh (a d) (== `(,a . ,d) quasi-p) (=/= 'unquote a) (== penv penv-out) (literalo mval))) ((fresh (a d v1 v2 penv^) (== `(,a . ,d) quasi-p) (=/= 'unquote a) (== `(,v1 . ,v2) mval) (conde ((quasi-p-no-match a v1 penv penv^)) ((quasi-p-match a v1 penv penv^) (quasi-p-no-match d v2 penv^ penv-out))))))) (define initial-env `((list . (val . (closure (lambda x x) ,empty-env))) (not . (val . (prim . not))) (equal? . (val . (prim . equal?))) (symbol? . (val . (prim . symbol?))) (cons . (val . (prim . cons))) (null? . (val . (prim . null?))) (car . (val . (prim . car))) (cdr . (val . (prim . cdr))) . ,empty-env))
numbers
;; number relations based on "oleg numbers" (define (appendo x y z) (conde ((== x '()) (== y z)) ((fresh (head xtail ytail ztail) (== x `(,head . ,xtail)) (== z `(,head . ,ztail)) (appendo xtail y ztail))))) (define zeroo (lambda (n) (== '() n))) (define poso (lambda (n) (fresh (a d) (== `(,a . ,d) n)))) (define >1o (lambda (n) (fresh (a ad dd) (== `(,a ,ad . ,dd) n)))) (define full-addero (lambda (b x y r c) (conde ((== 0 b) (== 0 x) (== 0 y) (== 0 r) (== 0 c)) ((== 1 b) (== 0 x) (== 0 y) (== 1 r) (== 0 c)) ((== 0 b) (== 1 x) (== 0 y) (== 1 r) (== 0 c)) ((== 1 b) (== 1 x) (== 0 y) (== 0 r) (== 1 c)) ((== 0 b) (== 0 x) (== 1 y) (== 1 r) (== 0 c)) ((== 1 b) (== 0 x) (== 1 y) (== 0 r) (== 1 c)) ((== 0 b) (== 1 x) (== 1 y) (== 0 r) (== 1 c)) ((== 1 b) (== 1 x) (== 1 y) (== 1 r) (== 1 c))))) (define addero (lambda (d n m r) (conde ((== 0 d) (== '() m) (== n r)) ((== 0 d) (== '() n) (== m r) (poso m)) ((== 1 d) (== '() m) (addero 0 n '(1) r)) ((== 1 d) (== '() n) (poso m) (addero 0 '(1) m r)) ((== '(1) n) (== '(1) m) (fresh (a c) (== `(,a ,c) r) (full-addero d 1 1 a c))) ((== '(1) n) (gen-addero d n m r)) ((== '(1) m) (>1o n) (>1o r) (addero d '(1) n r)) ((>1o n) (gen-addero d n m r))))) (define gen-addero (lambda (d n m r) (fresh (a b c e x y z) (== `(,a . ,x) n) (== `(,b . ,y) m) (poso y) (== `(,c . ,z) r) (poso z) (full-addero d a b c e) (addero e x y z)))) (define pluso (lambda (n m k) (addero 0 n m k))) (define minuso (lambda (n m k) (pluso m k n))) (define *o (lambda (n m p) (conde ((== '() n) (== '() p)) ((poso n) (== '() m) (== '() p)) ((== '(1) n) (poso m) (== m p)) ((>1o n) (== '(1) m) (== n p)) ((fresh (x z) (== `(0 . ,x) n) (poso x) (== `(0 . ,z) p) (poso z) (>1o m) (*o x m z))) ((fresh (x y) (== `(1 . ,x) n) (poso x) (== `(0 . ,y) m) (poso y) (*o m n p))) ((fresh (x y) (== `(1 . ,x) n) (poso x) (== `(1 . ,y) m) (poso y) (odd-*o x n m p)))))) (define odd-*o (lambda (x n m p) (fresh (q) (bound-*o q p n m) (*o x m q) (pluso `(0 . ,q) m p)))) (define bound-*o (lambda (q p n m) (conde ((== '() q) (poso p)) ((fresh (a0 a1 a2 a3 x y z) (== `(,a0 . ,x) q) (== `(,a1 . ,y) p) (conde ((== '() n) (== `(,a2 . ,z) m) (bound-*o x y z '())) ((== `(,a3 . ,z) n) (bound-*o x y z m)))))))) (define =lo (lambda (n m) (conde ((== '() n) (== '() m)) ((== '(1) n) (== '(1) m)) ((fresh (a x b y) (== `(,a . ,x) n) (poso x) (== `(,b . ,y) m) (poso y) (=lo x y)))))) (define
1o m)) ((fresh (a x b y) (== `(,a . ,x) n) (poso x) (== `(,b . ,y) m) (poso y) (
1o b) (=lo n b) (pluso r b n)) ((== '(1) b) (poso q) (pluso r '(1) n)) ((== '() b) (poso q) (== r n)) ((== '(0 1) b) (fresh (a ad dd) (poso dd) (== `(,a ,ad . ,dd) n) (exp2 n '() q) (fresh (s) (splito n dd r s)))) ((fresh (a ad add ddd) (conde ((== '(1 1) b)) ((== `(,a ,ad ,add . ,ddd) b)))) (
1o n) (== '(1) q) (fresh (s) (splito n b s '(1)))) ((fresh (q1 b2) (== `(0 . ,q1) q) (poso q1) (
1o q) (fresh (q1 nq1) (pluso q1 '(1) q) (repeated-mul n q1 nq1) (*o nq1 n nq)))))) (define expo (lambda (b q n) (logo n b q '()))) ;; factor the number 420 by running multiplication backwards! (*o x y '(0 0 1 0 0 1 0 1 1))
natural language grammar
;; constructing sentences using relations (define (conso car cdr c) (== `(,car . ,cdr) c)) (define (membero x l) (fresh (head tail) (== l `(,head . ,tail)) (conde ((== x head)) ((membero x tail))))) (define (nouno n) (membero n '(cat bat))) (define (verbo v) (membero v '(eats))) (define (deto d) (membero d '(the a))) ;; fixed sentence structure (define (my-sentenceo s) (fresh (v n1 d1 n2 d2) (verbo v) (nouno n1) (nouno n2) (deto d1) (deto d2) (== s `(,d1 ,n1 ,v ,d2 ,n2)))) (my-sentenceo x) ;; with parameterized structure (define (wordo class word) (conde ((== class 'noun) (nouno word)) ((== class 'verb) (verbo word)) ((== class 'det) (deto word)))) (define (sentenceo grammar s) (conde ((== grammar '()) (== s '())) ((fresh (g-head s-head g-tail s-tail) (conso g-head g-tail grammar) (conso s-head s-tail s) (wordo g-head s-head) (sentenceo g-tail s-tail))))) (sentenceo '(det noun verb det noun) s)
resources
miniKanren website: http://minikanren.org/ Daniel P. Friedman, William E. Byrd and Oleg Kiselyov The Reasoned Schemer. The MIT Press, Cambridge, MA, 2005. https://mitpress.mit.edu/index.php?q=books/reasoned-schemer Jason Hemann and Daniel P. Friedman. microKanren: A Minimal Functional Core for Relational Programming. In Proceedings of the 2013 Workshop on Scheme and Functional Programming (Scheme '13), Alexandria, VA, 2013. http://webyrd.net/scheme-2013/papers/HemannMuKanren2013.pdf William E. Byrd Relational Programming in miniKanren: Techniques, Applications, and Implementations. Indiana University, Bloomington, IN, September 30, 2009. http://gradworks.umi.com/3380156.pdf William E. Byrd, Eric Holk, and Daniel P. Friedman. miniKanren, Live and Untagged: Quine Generation via Relational Interpreters (Programming Pearl). In the Proceedings of the 2012 Workshop on Scheme and Functional Programming, Copenhagen, Denmark, 2012. http://webyrd.net/quines/quines.pdf PolyConf 15 talk : The Promise of Relational Programming William E. Byrd https://www.youtube.com/watch?v=eQL48qYDwp4 William E. Byrd Relational Programming in miniKanren, Part 1 and Part 2 Logic Night Lambda Lounge Utah, Sandy, UT, May 13, 2014. https://www.youtube.com/watch?v=zHov3fKYqBA https://www.youtube.com/watch?v=nFE2E91VDAk Code Mesh 2015 talk: Philip Wadler Propositions as Types https://www.youtube.com/watch?v=OGF-TGd-CIo&list=PLWbHc_FXPo2jB6IZ887vLXsPoympL3KEy&index=11 The Art of Prolog https://mitpress.mit.edu/books/art-prolog Hanne Riis Nielson, Flemming Nielson: Semantics with Applications: A Formal Introduction. Wiley Professional Computing, (240 pages, ISBN 0 471 92980 8), Wiley, 1992. http://www.daimi.au.dk/~bra8130/Wiley_book/wiley.html M. Mitchell Waldrop. 2001. The Dream Machine: J.C.R. Licklider and the Revolution that Made Computing Personal. Viking Penguin. As We May Thunk http://webyrd.net/thunk.html https://groups.google.com/d/forum/as-we-may-thunk
scratch 1
;; scratch 1
scratch 2
;; scratch 2
scratch 3
;; scratch 3
scratch 4
;; scratch 4
scratch 5
;; scratch 5
demos:
create link
font size:
+
-