Eval troubles

72 views
Skip to first unread message

Miron Brezuleanu

unread,
Sep 3, 2009, 2:24:33 AM9/3/09
to Clojure
Hello,

I'm a Clojure newbie (many thanks to Rich Hickey and everyone involved
- it's a great programming environment) and I have some trouble with
'eval'.

What I'm trying is:
$ java -cp clojure.jar clojure.lang.Repl
Clojure 1.1.0-alpha-SNAPSHOT
user=> (let [x 1] (eval '(inc x)))
java.lang.Exception: Unable to resolve symbol: x in this context
(NO_SOURCE_FILE:1)

(on a freshly downloaded&compiled clojure from github)

According to my understanding of http://clojure.org/evaluation, my
code should simply return 2.

What am I missing? :-)

Christian Vest Hansen

unread,
Sep 3, 2009, 7:02:28 AM9/3/09
to clo...@googlegroups.com
You need to unquote the x to get its value from the let. You can do
that with the tilde character inside syntax quotes:

user=> (let [x 1] (eval `(inc ~x)))
2
--
Venlig hilsen / Kind regards,
Christian Vest Hansen.

Jarkko Oranen

unread,
Sep 3, 2009, 8:07:40 AM9/3/09
to Clojure
On Sep 3, 9:24 am, Miron Brezuleanu <mbr...@gmail.com> wrote:
> Hello,
>
> I'm a Clojure newbie (many thanks to Rich Hickey and everyone involved
> - it's a great programming environment) and I have some trouble with
> 'eval'.
>
> What I'm trying is:
> $ java -cp clojure.jar clojure.lang.Repl
> Clojure 1.1.0-alpha-SNAPSHOT
> user=> (let [x 1] (eval '(inc x)))
> java.lang.Exception: Unable to resolve symbol: x in this context
> (NO_SOURCE_FILE:1)
>
> (on a freshly downloaded&compiled clojure from github)
>
> According to my understanding ofhttp://clojure.org/evaluation, my
> code should simply return 2.
>
> What am I missing? :-)

Eval has no access to its lexical environment at runtime; you can only
use global bindings.

The syntax-quote trick Christian showed works because it expands to
something similar to (eval (list 'inc x)), and when eval is called,
the (list 'inc x) is evaluated (as function parameters are), the value
of x in the lexical context is available and the argument to eval
becomes '(inc 2), which works. (nb. eval can evaluate the inc symbol
because it names a global Var.)

I hope I managed to explain this clearly enough. I'm not sure I could
understand this explanation if I didn't already. :)

--
Jarkko

Konrad Hinsen

unread,
Sep 3, 2009, 7:36:05 AM9/3/09
to clo...@googlegroups.com
On 3 Sep 2009, at 08:24, Miron Brezuleanu wrote:

> user=> (let [x 1] (eval '(inc x)))
> java.lang.Exception: Unable to resolve symbol: x in this context
> (NO_SOURCE_FILE:1)
>
> (on a freshly downloaded&compiled clojure from github)
>
> According to my understanding of http://clojure.org/evaluation, my
> code should simply return 2.

The critical point here is the environment in which eval evaluates its
argument. This is not specified on the documentation page you cite.
Eval uses the environment of the current namespace, not taking into
account the lexical environment surrounding the call to eval itself.
Said differently, the result is the same as if the argument to eval
were placed at the outermost expression level of the module inside
which eval is called.

Two examples that do work are

1) (eval '(let [x 1] (inc x)))
Here the lexical scope is created inside the evaluated expression.

2) (def y 2)
(eval '(inc y))
Here the argument to inc is defined in the namespace before eval is
called.

Konrad.

Miron Brezuleanu

unread,
Sep 3, 2009, 8:43:35 AM9/3/09
to clo...@googlegroups.com
Hello,

thanks everyone for replies,

On Thu, Sep 3, 2009 at 2:36 PM, Konrad Hinsen
<konrad...@fastmail.net> wrote:
>
> On 3 Sep 2009, at 08:24, Miron Brezuleanu wrote:
>
> > user=> (let [x 1] (eval '(inc x)))
> > java.lang.Exception: Unable to resolve symbol: x in this context
> > (NO_SOURCE_FILE:1)
> >
> > (on a freshly downloaded&compiled clojure from github)
> >
> > According to my understanding of http://clojure.org/evaluation, my
> > code should simply return 2.
>
> The critical point here is the environment in which eval evaluates its
> argument. This is not specified on the documentation page you cite.
> Eval uses the environment of the current namespace, not taking into
> account the lexical environment surrounding the call to eval itself.
> Said differently, the result is the same as if the argument to eval
> were placed at the outermost expression level of the module inside
> which eval is called.
>

Turns out my question was asked before, I just used the wrong keywords
when searching (local environment instead of local bindings).

I'm interested in eval because I thought it would be nice if we could
use something like Python's code.interact() for debugging. Basically,
get a repl inside an executing function, that has access to the
environment of the function. Poor man's debugger :-)

I guess that is rather hard to obtain, since eval executes in a
'global' context and there is no way to specify an environment in
which to execute the code. That could be simulated, if it was a way to
reach to the environment of a function (as a map of symbols to
values). Then the expression to eval could be bound in a let,
something like this:

;; assuming that the local environment is {'a 1 'b 2}
;; and we want to execute (+ a b)
;; we would have to generate something like
`(let [a ~(get-local-value 'a)
b ~(get-local-value 'b)]
(+ a b)

and 'eval' that.

Is there a way to get the list of symbols bound locally and to access
their values?

... Or a better way to simulate code.interact() ?

Thanks,
--
Miron Brezuleanu

Konrad Hinsen

unread,
Sep 3, 2009, 9:26:57 AM9/3/09
to clo...@googlegroups.com
On 3 Sep 2009, at 14:43, Miron Brezuleanu wrote:

> Is there a way to get the list of symbols bound locally and to access
> their values?

I don't think so. Python and Clojure are quite different languages.
Python is much more dynamic, with variable lookup happening at
runtime. In Clojure, only references to global variables are resolved
at runtime. Everything else, in particular lexical environments, are
resolved at compile time. Once your code is running, all the local
symbols are gone.

There are good and bad aspects to both choices, as so often. Python's
dynamic-to-the-end approach facilitates debugging, Clojure's compile-
as-much-as-possible attitude yields faster code and better compile-
time diagnostics.

> .. Or a better way to simulate code.interact() ?

You would probably have to write a complete Clojure interpreter. Given
Clojure's Lisp-ness, that's less of an effort than for other
languages, but it's still not a trivial project.

Konrad.

Miron Brezuleanu

unread,
Sep 3, 2009, 9:47:34 AM9/3/09
to clo...@googlegroups.com
Hi,

On Thu, Sep 3, 2009 at 4:26 PM, Konrad Hinsen<konrad...@fastmail.net> wrote:
>
> On 3 Sep 2009, at 14:43, Miron Brezuleanu wrote:
>
>> Is there a way to get the list of symbols bound locally and to access
>> their values?
>
> I don't think so. Python and Clojure are quite different languages.
> Python is much more dynamic, with variable lookup happening at
> runtime. In Clojure, only references to global variables are resolved
> at runtime. Everything else, in particular lexical environments, are
> resolved at compile time. Once your code is running, all the local
> symbols are gone.

I was afraid of that (the fact that eval didn't have an 'env' argument
as in more Scheme-like languages was hinting to the fact that access
to the environment is not easy after compilation).

>
> There are good and bad aspects to both choices, as so often. Python's
> dynamic-to-the-end approach facilitates debugging, Clojure's compile-
> as-much-as-possible attitude yields faster code and better compile-
> time diagnostics.
>
>> .. Or a better way to simulate code.interact() ?
>
> You would probably have to write a complete Clojure interpreter. Given
> Clojure's Lisp-ness, that's less of an effort than for other
> languages, but it's still not a trivial project.

Well, I don't need a debugger _that_ much. :-) I was just trying to
make sure I don't end up not using a code.interact() equivalent
because I missed some Clojure API.

Thanks,
--
Miron Brezuleanu

James Sofra

unread,
Sep 3, 2009, 9:01:17 PM9/3/09
to Clojure
Hi,

I am not familiar with the Python code.interact() thing and what it
does so I may be missing something but if you are looking to do
debugging is there a reason you can't use a Java debugger to debug
your Clojure code? (I have heard some people have had success for
JSwat.) I guess that is not the same as having a REPL.

Cheers,
James

On Sep 3, 11:47 pm, Miron Brezuleanu <mbr...@gmail.com> wrote:
> Hi,
>

Stuart Sierra

unread,
Sep 4, 2009, 1:04:02 AM9/4/09
to Clojure
On Sep 3, 9:26 am, Konrad Hinsen <konrad.hin...@fastmail.net> wrote:
> I don't think so. Python and Clojure are quite different languages.  
> Python is much more dynamic, with variable lookup happening at  
> runtime.

Or, more simply, Python is an interpreter, Clojure is a compiler. So
Clojure's "eval" actually compiles the form into Java bytecode, then
executes it.

-SS

Miron Brezuleanu

unread,
Sep 4, 2009, 1:43:36 AM9/4/09
to clo...@googlegroups.com
Hello,

On Fri, Sep 4, 2009 at 4:01 AM, James Sofra<james...@gmail.com> wrote:
>
> Hi,
>
> I am not familiar with the Python code.interact() thing and what it
> does so I may be missing something but if you are looking to do
> debugging is there a reason you can't use a Java debugger to debug
> your Clojure code? (I have heard some people have had success for
> JSwat.) I guess that is not the same as having a REPL.

A basic code.interact sample (the >>> used by Python as prompt may
look like email quotations in some clients):
IDLE 1.2.4
>>> import code
>>> def testCodeInteract():
a = 1
b = 2
code.interact("Test Code Interact", raw_input, locals())


>>> a <--- this is the IDLE repl

Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
a
NameError: name 'a' is not defined
>>> testCodeInteract()
Test Code Interact
>>> a <--- this is the 'inner' code.interact() repl, 'a' is bound here
1
>>> a + b
3
>>> <--- pressed Ctrl-D to exit the inner repl

>>> <--- outer repl again

I'll take a look at JSwat, even though I'm quite comfortable with a
REPL in Emacs to test small pieces of code (plus automated tests
later) and 'printf debugging' - the problem is they are sometimes not
enough at the worst moments, so a more capable debugger would come in
handy.

Thanks,
--
Miron Brezuleanu

Miron Brezuleanu

unread,
Sep 4, 2009, 1:55:12 AM9/4/09
to clo...@googlegroups.com
Hello,

I'm not sure this is an interpreter/compiler issue :-) I think it is
more of a resource allocation problem, i.e. what features to add to
Clojure and when.

The code.interact() trick is not specific to Python (or interpreted
languages). I guess it would be implementable in Schemes that provide
the environment as an argument to eval, and some of them are compiled.
Smalltalk also has this, and there is the "Immediate Window" in Visual
Studio which provides a C# REPL at any point on the callstack after
hitting a breakpoint (similar to the Smalltalk debugger). I'm not
familiar with the details of the "Immediate Window" implementation, I
assume they make good use of debugging information.

Anyway, Clojure is great even without this feature - maybe it will be
added at some point in the future. I sure don't mind the extra speed
gained from compiling as much as possible :-)

Thanks,
--
Miron Brezuleanu

Konrad Hinsen

unread,
Sep 4, 2009, 2:34:31 AM9/4/09
to clo...@googlegroups.com
On 4 Sep 2009, at 07:04, Stuart Sierra wrote:

> Or, more simply, Python is an interpreter, Clojure is a compiler. So
> Clojure's "eval" actually compiles the form into Java bytecode, then
> executes it.

I'd say both Python and Clojure are somewhere in between the classical
extremes of "interpreter" and "compiler". The extreme cases are the
easiest to understand, but newcomers to both Python and Clojure often
have a hard time to figure out what exactly happens when and what the
consequences are. Perhaps tutorials should address this issue more
clearly.

Konrad.

Stuart Sierra

unread,
Sep 4, 2009, 9:30:48 AM9/4/09
to Clojure
On Sep 4, 1:55 am, Miron Brezuleanu <mbr...@gmail.com> wrote:
> I'm not sure this is an interpreter/compiler issue :-) I think it is
> more of a resource allocation problem, i.e. what features to add to
> Clojure and when.

True, it's that Clojure does not have first-class environments, either
dynamic or lexical. I think it's been discussed, but not very
seriously.

-SS
Reply all
Reply to author
Forward
0 new messages