Symbol evaluation error in let

9 views
Skip to first unread message

Eduardo Julian

unread,
Nov 27, 2010, 10:45:21 AM11/27/10
to Clojure
user=> (let [a 'b] (str a))
"b"
user=> (let [b 5 a 'b] (eval a))
java.lang.Exception: Unable to resolve symbol: b in this context
(repl-1:7)
user=> (let [a 'b b 5] (eval a))
java.lang.Exception: Unable to resolve symbol: b in this context
(repl-1:9)

user=> (def b 5)
#'user/b
user=> (def a 'b)
#'user/a
user=> a
b
userr=> (eval a)
5

How come this problem happens inside the let but not with def?

Ken Wesson

unread,
Nov 27, 2010, 4:31:29 PM11/27/10
to clo...@googlegroups.com

If you

(do
(binding [*ns* (find-ns 'foo)]
(eval bar)))

the evaluation of bar sees the global bindings in the namespace foo,
and can get at other global bindings and Java classes with
fully-qualified names, but cannot see the lexical environment around
the eval call. Even when called inside a let or a function body,
eval's contents only see what they would see if executed at the top
level of the source file for the namespace that is *current when eval
is called* (not necessarily the same file the eval itself is in).

Ken Wesson

unread,
Nov 27, 2010, 5:24:23 PM11/27/10
to clo...@googlegroups.com

Adding to this, you can work around it with

(defmacro eval-with-local-vars [vars sexp]
(let [quoted-vars (vec (map #(list 'quote %) vars))]
`(let [varvals# (vec (interleave ~quoted-vars ~vars))]
(eval (list 'clojure.core/let varvals# ~sexp)))))

user=> (let [a 1 b 2] (eval-with-local-vars [a b] '(+ a b)))
3

user=> (let [b 5 a 'b] (eval-with-local-vars [b a] a))
5

Note that the vars have to be in the correct order in the vector:

user=> (let [b 5 a 'b] (eval-with-local-vars [a b] a))
#<CompilerException java.lang.Exception: Unable to resolve symbol: b
in this context (NO_SOURCE_FILE:161)>

Eduardo Julian

unread,
Nov 27, 2010, 7:56:56 PM11/27/10
to Clojure
Woah. That's as weird as you can get.

Thanks, man.

Ken Wesson

unread,
Nov 27, 2010, 9:32:27 PM11/27/10
to clo...@googlegroups.com
Caveat: since eval-with-local-vars is a macro you won't be able to
directly use it in HOFs. Wrapping it in a closure works, however:

user=> (let [a 1 b 2] (map #(eval-with-local-vars [a b] %) ['(+ a b) '(* a b)]))
(3 2)

Sunil S Nandihalli

unread,
Nov 29, 2010, 7:52:51 AM11/29/10
to clo...@googlegroups.com
That was really neat ken ... eval-with-local-vars.. I think it should be part of the core...
Sunil.


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Sunil S Nandihalli

unread,
Nov 29, 2010, 8:31:37 AM11/29/10
to clo...@googlegroups.com
Hi Ken,
Just wrapping your macro with the following would save you the trouble of having to enumerate the local variables ...
(defmacro eval-with-local-bindings [sexp]
  `(eval-with-local-vars ~(apply vector (keys &env)) ~sexp))
Sunil.

Ken Wesson

unread,
Nov 29, 2010, 4:10:57 PM11/29/10
to clo...@googlegroups.com
On Mon, Nov 29, 2010 at 8:31 AM, Sunil S Nandihalli
<sunil.na...@gmail.com> wrote:
> Hi Ken,
> Just wrapping your macro with the following would save you the trouble of
> having to enumerate the local variables ...
> (defmacro eval-with-local-bindings [sexp]
>   `(eval-with-local-vars ~(apply vector (keys &env)) ~sexp))

Yeah, nifty. I didn't know about &env when I wrote eval-with-local-vars. :)

Reply all
Reply to author
Forward
0 new messages