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.
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:
(print-dup (fn [] 1) *out*)
;; #=(user$eval24491$fn__24492. )
(let [x 1] (print-dup (fn [] x) *out*))
;; #=(user$eval24497$fn__24498. )
Clojure closures are implemented via function-classes which accept their closed-over variable values as constructor arguments. Hence:
(let [f (fn [] 1)] (eval `(~f)))
;; 1
(let [x 1, f (fn [] x)] (eval `(~f)))
;; IllegalArgumentException No matching ctor found ...
So now you know, and know why to avoid directly inserting function objects into generated code, even when it "works."
See this example that does also throw the exception:
(defmacro does-also-not-work []
(let [x 4
f (fn [] x)]
`(~f)))
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With