clojureclojure-core.logicminikanren

Extracting finite domain lvars from a map


I want to put fresh lvars with a finite domain into a map, and establish a relationship between them in another part of my code. Consider the snippet below:

(l/run 1 [q]
       (l/fresh [x y z a b c]
                (fd/in x y z (fd/interval 0 100)) ; establish domain for x y z
                (let [w {:a x :b y :c z}] ; store x y z in a map
                  (l/all
                    (l/featurec w {:a a :b b :c c}) ; extract x y z as a b c
                    (fd/+ a b c))) ; a relationship
                (l/== q [a b c])))

==> Error printing return value at clojure.core.logic/verify-all-bound$verify-all-bound* (logic.clj:2136).
Constrained variable <lvar:a__5787> without domain

Is there a way to accomplish this?


Solution

  • Featurec isn't designed to allow deriving values (see link's example in the core.logic/featurec documentation).

    In general, you can expect core.logic functions that end with a 'c' to be 'constraint functions' specialized towards filtering ('constraining') a set/domain of possible values already derived from previous logic functions. It's a useful naming convention from the library authors, and from what I've seen users of core.logic try to stick to it as well.

    Normal unification works fine for this particular problem, though.

    (l/run* [q]
       (l/fresh [x y z a b c]
                (fd/in x y z (fd/interval 0 3)) ; establish domain for x y z
                (let [w {:a x :b y :c z}] ; store x y z in a map
                  (l/all
                    (l/== w {:a a :b b :c c}) ; extract x y z as a b c
                    (fd/+ a b c))) ; a relationship
                (l/== q [a b c])))
    => ([0 0 0] [1 0 1] [0 1 1] [0 2 2] [2 0 2] [1 1 2])
    

    Or, if you only want certain values, just extract the lvars and unify individually:

    (l/run* [q]
       (l/fresh [x y z a b c]
                (fd/in x y z (fd/interval 0 2)) ; establish domain for x y z
                (let [w {:a x :b y :c z}] ; store x y z in a map
                  (l/all
                    (l/== a (w :a)) (l/== b (w :b)) ; extract x y z as a b c
                    (fd/+ a b c))) ; a relationship
                (l/== q [a b c]) ))
    => ([0 0 0] [1 0 1] [0 1 1] [0 2 2] [2 0 2] [1 1 2] [1 2 3] [2 1 3] [2 2 4])
    

    (also note that we didn't unify c and z, so c can be outside the interval)

    If you want, you can make a list of the lvars and corresponding keys you want, and then use everyg to add a relation for each pair:

    (l/run* [q]
      (l/fresh [x y z a b c]
        (fd/in x y z (fd/interval 0 2)) ; establish domain for x y z
        (let [w {:a x :b y :c z}
              targlist [a b c]
              wantedlist [:a :b :c]] ; store x y z in a map
          (l/all
           (everyg #(l/==
                     (get targlist %)
                     (w (get wantedlist %)))
                   (range (count targlist))) ; extract x y z as a b c
           (apply fd/+ [a b c]))) ; a relationship
        (l/== q [a b c])))
    => ([0 0 0] [1 0 1] [0 1 1] [0 2 2] [2 0 2] [1 1 2])