Two following examples of using a function in a macro result in evaluations without errors.
(defmacro works []
(let [f (fn [] 1)]
`(~f)))
(works)
;; => 1
(defn my-nullary-fn []
(fn [] 2))
(defmacro also-works []
(let [f (my-nullary-fn)]
`(~f)))
(also-works)
;; => 2
However,
(defmacro does-not-work []
(let [f (constantly 3)]
`(~f)))
(does-not-work)
throws
java.lang.IllegalArgumentException: No matching ctor found
for class clojure.core$constantly$fn__4051
Likewise,
(defn my-unary-fn [x]
(fn [] x))
(defmacro also-does-not-work []
(let [f (my-unary-fn 4)]
`(~f)))
(also-does-not-work)
throws
java.lang.IllegalArgumentException No matching ctor found
for class user$my_other_fn$fn__12802
What might be the reason? Is there a difference between function objects returned by fn
, my-nullary-fn
, constantly
and my-unary-fn
?
I'm running Clojure 1.5.1.
CLJ-946 might be related.
See this example that does also throw the exception:
Just like the result of
constantly
, but unlike your first two examples,f
here is a closure. Apparently, closures created during macro-expansion time do not persist.Take a look at
clojure.lang.Compiler.ObjExpr#emitValue()
. Any instance objects which appear directly in code (or generated code, in the case of macro-expansion results) must either:print-dup
defined for their type, in which case the compiler emits object instantiation via round-tripping through the reader.Function objects do have a
print-dup
implementation, but it constructs read-eval forms which only call the 0-argument version of the function class constructor:Clojure closures are implemented via function-classes which accept their closed-over variable values as constructor arguments. Hence:
So now you know, and know why to avoid directly inserting function objects into generated code, even when it "works."