clojurelogicclojure-core.logic

core.logic explain how `fresh` changes results


Just starting out with core.logic, version "0.8.11":

  (q/run* [q]
    (q/fresh [a]
      (q/membero a [2 3]))
    (q/membero q [1]))

I don't understand the result: (1 1).

My understanding is that I create another variable a with fresh, which can take a value of 2 or 3. And q can take a value of 1. Therefore I was expecting to see something like: (1), or (1 2 1 3), or maybe ([1 2] [1 3]) or even ({:q 1 :a 2} {:q 1 :a 3}), but not the actual result.

Another example:

  (q/run* [q]
    (q/fresh [a]
      (q/membero a [1 2 3])
      (q/membero q [3 4 5])
      (q/== a q)))
  ;; make sense to me, returns (3)

  (q/run* [q]
    (q/fresh [a]
      (q/membero a [1 2 3]))
    (q/membero q [3 4 5]))
  ;; does not make sense to me, returns (3 4 3 5 4 3 5 4 5)
  ;; I was expecting `(3 4 5)`

Could someone explain what is happening here?


Solution

  • core.logic can be viewed a search algorithm, searching for possible ways to assign values to all the relevant variables that don't cause a conflict. It has a lot of very smart pruning techniques so that it doesn't search subtrees that are known to be no good, but basically it is a search.

    It found two ways to assign variables that satisfy your query:

    So it is returning two results. But you only ask about q (that's what the argument to run* is, the set of variables you want to know the values of), and q is the same in both of those assignments, so you see the same result (1) twice. You should not in general assume that results from run* will be distinct, unless you have gone to some effort to make them so.

    Likewise in your last example, there are three values you could assign to q, and each of them works for any of the three values you could assign to a, so you get 3*3=9 results, with each q value repeated three times. The order these results are returned in is (from your perspective) arbitrary; really they are ordered in a way that helps core.logic prevent getting deadlocked in other, more complicated programs. You could see all the a,q pairs if you wanted, by writing instead (q/run* [a q] ...).