I announced it earlier this week on Twitter, but it's now come far along enough to be usable. You can write fun stuff like the following:
(defn likes
[x y]
(cond-e
((== x 'john) (== y 'mary))
((== x 'mary) (== y 'john))))
(defn musician [x]
(cond-e
((== x 'john))
((== x 'bob))))
(run* [q]
(likes q 'mary)
(musician q))
;; [john]
;; [john bob]
(run* [q]
(exist [x y]
(likes x y)
(== [x y] q)))
;; [[john mary] [mary john]]
My inspiration to start on this was Jim Duey's implementation. However I had two specific goals -
* Focus on performance. While developing I compared performance against miniKanren under Racket - I made sure the Clojure implementation ran at least as fast, and in many cases it runs quite a bit faster
* Implement "growable" sequences without resorting to Scheme's dotted pairs. This required me to develop a new protocol in which iteration might return a logic var instead of nil or a Seq.
The implementation started with the one described William Byrd's dissertation. However a considerable number of changes were made-
* substitutions are implemented as a protocol and a defrecord
* substitutions internally use a PersistentHashMap as well as PersistentVector (for debugging)
* supports unification of all the Clojure data structures supported by clojure.walk
* goal and goal constructors are implements as deftypes (Mzero, Unit, Choice, Inc) and those implement IMPlus and IBind protocols.
The code base is compact, some ~450 lines of Clojure.
Future directions:
* Friendlier interface for defining facts and running queries
* There are many great ideas in William Byrd's thesis that need looking into. In particular I'm interested in tabling.
* Investigating replacing the PersistentHashMap with a Skew Binary Random Access List or a Finger Tree. This would speed up the performance of the most expensive functions in the system, walk. In the Scheme implementations SBRALs can lead to 2.5X-100X in performance.
* Modifying the core macros to optimize logic programs.
* Parallel logic programming.