Search code examples
clojureclojure-core.logic

How to rewrite the core.logic snippet?


I'm trying to rewrite the below piece of core.logic code.

(run* [x y]
    (fd/in x (fd/domain 1 2))
    (fd/in y (fd/domain 1 2)))

o/p,

([1 1] [2 1] [1 2] [2 2])

I tried the below versions but none of them works,

(run* [x y]
    (fresh [dom (fd/domain 1 2)])
        (fd/in x dom)
        (fd/in y dom)))

;; Error Unsupported binding form: (fd/domain 1 2)

(run* [x y]
 (fresh [dom]
    (== dom  (fd/domain 1 2))
    (fd/in x dom)
    (fd/in y dom)))

O/P:

([1 1])

(run* [x y]
  (let [dom (fd/domain 1 2)]
  (fd/in x dom)
  (fd/in y dom)))

O/P:

([_0 1] [_0 2])

What's the rationale for the 3 versions that I tried? Any help would be greatly appreciated.


Solution

  • fd/domain returns a concrete value that can be used with other goals/relations in the fd namespace — you can define it once and use it inside run* more than once:

    (let [dom (fd/domain 1 2)]
      (run* [x y]
        (fd/in x dom)
        (fd/in y dom)))
    => ([1 1] [2 1] [1 2] [2 2])
    

    What's the rationale for the 3 versions that I tried?

    The first refactoring doesn't work because fresh is being used like let, but it doesn't work like that; fresh simply allows you to give names to some fresh logic variables.

    The second refactoring doesn't work because the domain value is being bound to a logic variable, and fd/in wants a concrete domain value as its second argument — not a (fresh) logic variable.

    The third refactoring doesn't work (I assume) because let bindings aren't going to work like that inside the run* macro, which only wants a sequence of goals in its body.