Josh.F
Josh.F

Reputation: 3806

In Clojure core.logic, why can't I use threading macros?

When I run the following code, I get the ensuing error:

(run 3 [q]
  (fresh [a0 a1 a2
          b0 b1 b2
          c0 c1 c2]
    (== q [[a0 a1 a2] [b0 b1 b2] [c0 c1 c2]])
    (fd/in a0 a1 a2 b0 b1 b2 c0 c1 c2 (fd/interval 1 9))
    (fd/distinct [a0 a1 a2 b0 b1 b2 c0 c1 c2])
    (fd/eq
     (= a0 4)
     (= 22 (-  (* a0 a1) a2))
     (= -1 (-> b0 (* b1) (- b2)))
     )))

error:

2. Unhandled clojure.lang.Compiler$CompilerException
   Error compiling src/euler/core.clj at (1392:5)

1. Caused by java.lang.IllegalArgumentException
   Can't call nil
 core.clj: 3081  clojure.core/eval
                  main.clj:  240  clojure.main/repl/read-eval-print/fn
                  main.clj:  240  clojure.main/repl/read-eval-print
                  main.clj:  258  clojure.main/repl/fn
                  main.clj:  258  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  clojure.tools.nrepl.middleware.interruptible-eval/evaluate/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  630  clojure.core/apply
                  core.clj: 1868  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
...

Notice the line with the threading macro ->, in CIDER I'll macro expand it and everything looks fine, but in the end the code crashes. I'm assuming this is the fault of macros, but I'm not sure why. Any ideas?

Upvotes: 3

Views: 128

Answers (1)

WolfeFan
WolfeFan

Reputation: 1447

You should look at the source of clojure.core.logic.fd. The error occurs in the macro eq, which process all of its forms before macroexpansion can occur.

For a quick solution, I've created a version of eq which calls macroexpand-all on all of its forms before doing anything else. It seems to work for your code, although I have not tested it in other contexts:

(defmacro mac-eq [& forms]
  (let [exp-forms (map clojure.walk/macroexpand-all forms)]
    `(fd/eq ~@exp-forms)))

Let's try it!

stack-prj.logicThreadMacro> (run 3 [q]
                              (fresh [a0 a1 a2
                                      b0 b1 b2
                                      c0 c1 c2]
                                (== q [[a0 a1 a2] [b0 b1 b2] [c0 c1 c2]])
                                (fd/in a0 a1 a2 b0 b1 b2 c0 c1 c2 (fd/interval 1 9))
                                (fd/distinct [a0 a1 a2 b0 b1 b2 c0 c1 c2])
                                (mac-eq
                                 (= a0 4)
                                 (= 22 (- (* a0 a1) a2))
                                 (= -1 (-> b0 (* b1) (- b2))))))
()

Upvotes: 3

Related Questions