Skip to content
David Nolen edited this page Dec 29, 2012 · 29 revisions

Simple in-memory database

Sometimes it's useful to create a list of facts that you want to run queries over. Use defrel and fact.

(defrel man p)
(fact man 'Bob)
(fact man 'John)
(fact man 'Ricky)

(defrel woman p)
(fact woman 'Mary)
(fact woman 'Martha)
(fact woman 'Lucy)
    
(defrel likes p1 p2)
(fact likes 'Bob 'Mary)
(fact likes 'John 'Martha)
(fact likes 'Ricky 'Lucy)

(defrel fun p)
(fact fun 'Lucy)

(run* [q]
  (fresh [x y]
    (fun y)
    (likes x y)
    (== q [x y]))) ; ([Ricky Lucy])

It's important to index relationships so that the time to run queries doesn't grow linearly with the number of facts. You can create indexes for any element of the fact tuple. Note that this has implications for memory usage.

; Clojure 1.3.0
(defrel likes ^:index p1 ^:index p2)

; Clojure 1.2.0
(defrel likes ^{:index true} p1 ^{:index true} p2)

** A la carte unifier core.logic comes with a unifier that can be used much like core.unify:

(unifier '(?x ?y ?z) '(1 2 ?y)) ; (1 2 _.0)

The above is quite slow since we have to walk the data structure and replace the logic var symbols. It's more efficient to prep the expressions before hand if you're going to be unifying the same expressions over and over again.

(let [[u w] (map prep ['(?x ?y) (1 2)])]
  (unifier* u w))

Tabling

core.logic as of version 0.5.4 supports tabling. Certain kinds of logic programs that would not terminate in Prolog will terminate in core.logic if you create a tabled goal.

(defne arco [x y]
  ([:a :b])
  ([:b :a])
  ([:b :d]))

(def patho
  (tabled [x y]
    (conde
     [(arco x y)]
     [(fresh [z]
        (arco x z)
        (patho z y))])))

;; (:b :a :d)
(run* [q] (patho :a q))

Definite Clause Grammars (Experimental)

core.logic has Prolog-type Definite Clause Grammar syntax for parsing. Until core.logic gets support for environment trimming this feature should be considered for experimental use only.

(def-->e verb [v]
  ([[:v 'eats]] '[eats]))

(def-->e noun [n]
  ([[:n 'bat]] '[bat])
  ([[:n 'cat]] '[cat]))

(def-->e det [d]
  ([[:d 'the]] '[the])
  ([[:d 'a]] '[a]))

(def-->e noun-phrase [n]
  ([[:np d n]] (det d) (noun n)))

(def-->e verb-phrase [n]
  ([[:vp v np]] (verb v) (noun-phrase np)))

(def-->e sentence [s]
  ([[:s np vp]] (noun-phrase np) (verb-phrase vp)))

(run* [parse-tree]
  (sentence parse-tree '[the bat eats a cat] []))

;; ([:s [:np [:d the] [:n bat]] [:vp [:v eats] [:np [:d a] [:n cat]]]])

Constraint Logic Programming (CLP)

core.logic has rapidly evolving support for different forms of constraint logic programming, CLP for short. core.logic is designed to be extensible to different constraint domains. Out of the box it supports disequality constraints over trees, known as CLP(Tree), and constraint over finite domains, know as CLP(FD).

CLP(Tree)

CLP(Tree) is pretty simple and adds only one new operator !=. Given two terms the use of the != operator will guarantee that the two terms will never unify, in some sense this is "opposite" of what is provided by the unification operator ==.

The most straightforward use is to just check that something is not not equal to some other simple value.

(run* [q]
  (!= q))

CLP(FD)