[CLJ-1206] 'eval' of closures or fns with runtime metadata within a call expr yields "No matching ctor found" exceptions Created: 28/Apr/13 Updated: 18/Feb/16
|Affects Version/s:||Release 1.5|
I ran into some issues with 'eval' when writing compilation strategies for Graph. It seems these may have been known for some time , but I couldn't find a ticket for them, so here we are.
Clojure docs  say "If the operator is not a special form or macro, the call is considered a function call. Both the operator and the operands (if any) are evaluated, from left to right," and "Any object other than those discussed above will evaluate to itself." While bare fns do seem to evaluate to themselves in all cases, when in a call expression, the evaluation of the operator fails on fn objects that are closures or have run-time metadata applied:
;; raw non-closures are fine
;; raw closures are fine
;; non-closures in exprs are fine
;; but closures in exprs cause an error
;; as do fns with metadata in exprs
|Comment by Dmitri Gaskin [ 17/Feb/16 9:25 PM ]|
I have run into this as well. I ran into it while using https://github.com/plumatic/schema + a memoized predicate function + https://github.com/metosin/compojure-api. After hours of debugging, I was able to narrow it down to:
IllegalArgumentException No matching ctor found for class clojure.core$memoize$fn__5708 clojure.lang.Reflector.invokeConstructor (Reflector.java:163)
|Comment by Kevin Downey [ 17/Feb/16 11:01 PM ]|
basically, this cannot work. as it stands now, for some set of fn objects (including closures) eval fails, because the compiler cannot embed arbitrary function objects in the emitted bytecode. for another set of fn objects eval succeeds, because there is a fall back in eval, where it tries to embed arbitrary objects in the byte code by invoking a no argument constructor on the class of the object. closures fail to work in that fall back because the classes generated for fns that are closures in clojure don't have 0-arg constructors.
don't eval function objects
|Comment by Kevin Downey [ 17/Feb/16 11:20 PM ]|
for a work around maybe try using dynamic binding?
|Comment by Jason Wolfe [ 18/Feb/16 12:35 AM ]|
Here is a workaround that we use in graph: https://github.com/plumatic/plumbing/blob/master/src/plumbing/graph/positional.clj#L45
Basically, rather than evaluating a form with function objects inside, e.g. `(eval (... f))`, we do: `((eval (fn [x] (... x))) f)`.