21

In Clojure,

(def x 3)
(eval '(prn x))

prints 3, whereas

(let [y 3]
   (eval '(prn y)))

and

(binding [z 3] (eval '(prn z)))

generate an 'Unable to resolve var' exception.

According to http://clojure.org/evaluation, eval, load-string, etc generate temporary namespaces to evaluate their contents. Therefore, I'd expect neither of the above code samples to work, since (def x 3) is done in my current namespace, not the one created by eval.

  1. Why does the first code sample work and not the last two?
  2. How can I eval a form with bound variables without using def?

Thanks!

Miikka
  • 4,573
  • 34
  • 47
gilesc
  • 1,969
  • 1
  • 14
  • 16

1 Answers1

17

1.:

The reason this doesn't work is (more or less) given on the page you linked:

It is an error if there is no global var named by the symbol […]

And:

[…]

  1. A lookup is done in the current namespace to see if there is a mapping from the symbol to a var. If so, the value is the value of the binding of the var referred-to by the symbol.

  2. It is an error.

eval evaluates forms in an empty (null in CL-lingo) lexical environment. This means, that you cannot access lexical variable bindings from the caller's scope. Also, binding creates new bindings for existing vars, which is why you cannot use it "by itself", without having declared or defed the variables you try to bind. Besides, lexical variables (at least in CL, but I would be surprised if this wasn't the case for Clojure) already ceased to exist at runtime – They are translated to addresses or values.

See also my older post about this topic.

2.:

So, you have to use dynamic variables. You can avoid the explicit def, but you still at least need to declare them (which defs var names without bindings):

user=> (declare ^:dynamic x)
#'user/x
user=> (binding [x 10] (eval '(prn x)))
10
nil

By the way: I suppose you know why you need eval, and that its use is considered evil when other solutions would be appropriate.

Taylor Wood
  • 15,886
  • 1
  • 20
  • 37
danlei
  • 14,121
  • 5
  • 58
  • 82
  • 3
    Note that this won't work in Clojure 1.3. You'll have to use `(declare ^:dynamic x)`. – danlei Jun 03 '11 at 00:44
  • Thanks! I understand my problem now -- I was assuming that "null lexical scope" also meant in a "null namespace", but quick testing reveals that `eval` works within the current namespace, which is why it has access to namespace vars but not lexical variables. Very nice answer, and your links were helpful as well! – gilesc Jun 03 '11 at 17:10
  • 1
    I guess you should quote the inner (prn x). – pkaleta Dec 22 '11 at 12:44
  • I ran into this issue while trying to write a macro that would automate something like `(let [x 41, y (inc x)] {:x x, :y y})` (obviously only interesting with large let blocks and lots of chewy math). I tried `(defmacro hashup [vars] ``(apply hash-map (map vector (map keyword ~vars) (map eval ~vars))))` (with just one backtick) and ran into the issue here. I don't see a way to DRY out the let block :( – Reb.Cabin Jul 13 '17 at 01:15
  • This works for me in the REPL. But if I wrap everything in a function and execute using `lein run`, it fails. – Punit Naik Jul 30 '18 at 12:32