How do you use core.logic in a practical way in a larger Clojure program?

How do you use core.logic in a practical way in a larger Clojure program?

The most important thing to remember is this: Relations are just function which return a goal.
A goal is a function which can succeed or fail, so basically relations are just higher order function.

Now you can make your example such that the relation and the various facts are in a single function and there is no global relation/facts which can interfere with each other:

(defn find-things []
  (letfn [(scoref [key value score]
            (conde
             [(== key :foo) (== value 10) (== score 2)]
             [(== key :bar) (== value 20) (== score 3)]
             [(== key :baz) (== value 30) (== score 7)]))]
    (run* [q]
          (conde 
           ((scoref :foo 10 q))
           ((scoref :baz 30 q))))))

score is just a function which return a goal (using the conde macro)

This solves the local/global relations problem, but still the facts and query are hardcoded into the function which we want to be passed as in parameters.
One possible way to do that is understand the core.logic APIs which allows you to define dynamic logic vars and unify them, etc. I have not been through that API so I wont be able to answer using that. Another way would be to use macro and eval magic:

(defmacro find-things-generator [data query]
  (let [key (gensym) value (gensym) score (gensym) q (gensym)]
    `(letfn [(~scoref [~key ~value ~score]
               (conde
                ~@(map #(-> [`(== ~key ~(% 0))
                             `(== ~value ~(% 1))
                             `(== ~score ~(% 2))]) data)
                ))]
       (run* [~q]
             (conde
              ~@(map #(-> [`(~scoref ~(% 0) ~(% 1) ~q)]) query)
              )))))


(defn find-things [data query]
  (eval `(find-things-generator ~data ~query)))

(def data [[:foo 1 2]
           [:bar 2 3]
           [:baz 3 7]])

(def query {:foo 1,
            :bar 2,
            :baz 3})

(find-things data query)

I had a similar question and here is what I came up with, translated to your problem.

Define your collection of scores.

(def scores
  [[:foo 10 2]
   [:bar 20 3]
   [:baz 30 7]])

Next, define a function that will convert the scores into relational form.

(defn scoreso [key value score scores]
  (conde
    [(fresh [a]
       (firsto scores a)
       (== [key value score] a))]
    [(fresh [d]
      (resto scores d)
       (scoreso key value score d))]))

Finally, determine which scores in the vector match given keys and values.

(run* [score]
  (fresh [key value]
    (scoreso key value score scores)
    (conde
      [(== key :foo) (== value 10)]
      [(== key :baz) (== value 30)])))

This results is (2 7).

The query is phrased differently, but it is equivalent.

How do you use core.logic in a practical way in a larger Clojure program?

Leave a Reply

Your email address will not be published. Required fields are marked *