I have a function spec defined like this, and I want to evaluate it into a function object so I can pass around.
(def spec '(foo [n] (* 2 n)))
I can create a macro like this
(defmacro evspec [name arg & body] `(defn ~name [~arg] ~@body))
then the following call will give me function foo. when called with 3, (foo 3), will return 6.
(evspec foo n (* 2 n))
However, if I get the function body from my spec defined above, the function foo returned wont evaluate the body form (* 2 n), instead, it return the body form.
(let [foo (first spec) arg (first (second spec)) body (last spec)]
(evspec foo arg body))
user=> (foo 3)
(* 2 n)
I notice the foo function created now is $eval$foo
user=> foo
#<user$eval766$foo__767 user$eval766$foo__767@39263b07>
while the working foo function is
user=> foo
#<user$foo user$foo@66cf7fda>
can anybody explain why is the difference and how can I make it work ? I want to have a way without replying on eval ? coming from javascript background, somehow I always think eval is evil.
It's simply not possible to do it in general without eval
. A macro is simply a function which is passed its argument expressions literally at compile time (when it is in general fundamentally impossible to know what their values might be at runtime). In particular, in the call to evspec
inside the let
form in the question text, where the return value is (* 2 n)
, the evspec
macro expander sees literally the symbol foo
and the symbol n
as its positional arguments and (body)
(a seq containing the single symbol body
) as its "rest" argument; the return value is in line with these inputs.
However, using eval
for this sort of purpose is perfectly fine. It's important to keep in mind that it has a considerable runtime cost, so you'll want to use it somewhat sparingly, but once you produce a function using eval
, it is a perfectly good Clojure function, just as fast as any other.
Also, note that while in JavaScript eval
operates on text, Clojure's eval
operates on Clojure data structures -- indeed the same data structures macros operate on -- which arguably makes it less error prone.
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