Search code examples
clojureclojure-core.logic

Clojure core.logic. Can I use complex data-structures in facts and rules?


I'm trying to get to grips with core.logic.

Can I use complex data-structures in facts and rules?

For example, I'm trying to do this :

(pldb/db-rel test a)

(defn is-wibble? [a] (= true (:wibble a)))

(def facts
  (pldb/db
   [test {:name "x" :wibble true}]
   [test {:name "y" :wibble false}]
   [test {:name "z" :wibble true}]))

(defn -main [& args]
  (doseq [x
          (pldb/with-db facts
            (run* [q]
              (is-wibble? q)))]
    (println x))))

But it's throwing an error :

Caused by: java.lang.ClassCastException: java.base/java.lang.Boolean cannot be cast to clojure.lang.IFn
    at clojure.core.logic.Substitutions.bind(logic.clj:425)
    at polvo.core$_main$fn__377$fn__378$fn__379$_inc__380.invoke(core.clj:223)

Actually at the line

(is-wibble? q)

Am I wrong to try to create rules as normal functions? Or put complex data into facts?


Solution

  • You can examine complex data structures in your db, the only missing piece here is that in your is-wibble? predicate you'll be receiving a logic variable instead of an actual, concrete value.

    There's a pred goal in core.logic that will project an lvar so you can examine its value. I renamed the goal to match typical goal names. pred takes an lvar and some function that will receive the lvar's value, and the pred goal succeeds if that predicate function returns truth-y.

    (defn wibbleo [a] (pred a :wibble))
    

    Or you could define it like this using your original predicate:

    (defn wibbleo [a] (pred a is-wibble?))    
    

    Note you also need to include your db-rel as a goal (test q), then your program should work:

    (pldb/with-db facts
      (run* [q]
        (test q)
        (wibbleo q)))
    => ({:name "x", :wibble true} {:name "z", :wibble true})