Confused by var eval behaviour

99 views
Skip to first unread message

Jamie Brandon

unread,
Aug 8, 2013, 9:40:38 AM8/8/13
to clo...@googlegroups.com
This has me stumped:

user=> (with-local-vars [x nil] (eval x))
#<Var: --unnamed-->
user=> (with-local-vars [x nil] (eval [x]))
CompilerException java.lang.NullPointerException, compiling:(NO_SOURCE_PATH:1:1)

By comparison:

user=> (def x nil)
#'user/x
user=> (eval #'x)
#'user/x
user=> (eval [#'x])
[#'user/x]

Christophe Grand

unread,
Aug 8, 2013, 9:58:04 AM8/8/13
to clojure
The error is caused by the fact that eval sees the var as a constant (or part of) and tries to serialize the constant.
#<Var: --unnamed--> is unreadable while #'user/x is that explains teh difference in behaviour.


--
--
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
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.





--
On Clojure http://clj-me.cgrand.net/
Clojure Programming http://clojurebook.com
Training, Consulting & Contracting http://lambdanext.eu/

Jamie Brandon

unread,
Aug 8, 2013, 10:44:12 AM8/8/13
to clo...@googlegroups.com
What if it isn't inside a constant?

user=> (eval `(fn [] ~#'x))
#<user$eval1366$fn__1367 user$eval1366$fn__1367@15dbb76>

user=> (with-local-vars [x nil] (eval `(fn [] ~x)))
CompilerException java.lang.NullPointerException, compiling:(NO_SOURCE_PATH:1:1)

How about functions? These are unreadable but they still eval
correctly inside constants.

user=> (letfn [(x [] nil)] (eval x))
#<user$eval1377$x__1378 user$eval1377$x__1378@5fea6729>

user=> (letfn [(x [] nil)] (eval [x]))
[#<user$eval1381$x__1382 user$eval1381$x__1382@e3b7c27>]

user=> (read-string "#<user$x user$x@1981e4d>")
RuntimeException Unreadable form clojure.lang.Util.runtimeException
(Util.java:219)

user=> (defn x [] nil)
#'user/x

user=> (eval x)
#<user$x user$x@14ff1714>

user=> (eval [x])
[#<user$x user$x@5892d4a8>]

user=> (read-string "#<user$eval1381$x__1382 user$eval1381$x__1382@e3b7c27>")
RuntimeException Unreadable form clojure.lang.Util.runtimeException
(Util.java:219)

Jamie Brandon

unread,
Aug 8, 2013, 10:51:28 AM8/8/13
to clo...@googlegroups.com
This actually seems to work for deftypes too:

user=> (deftype Foo [])
user.Foo
user=> (eval (Foo.))
#<Foo user.Foo@4450c45f>
user=> (eval [(Foo.)])
[#<Foo user.Foo@71c76cf5>]

But not for arbitrary objects:

user=> (eval (java.io.StringReader. "foo"))
#<StringReader java.io.StringReader@7e5ef8e8>

user=> (eval [(java.io.StringReader. "foo")])
CompilerException java.lang.RuntimeException: Can't embed object in
code, maybe print-dup not defined: java.io.StringReader@7504a5ce,
compiling:(NO_SOURCE_PATH:1:1)


So I guess Fn and Var have print-dup methods defined. I still don't
understand why print-dup is used at all though.

Christophe Grand

unread,
Aug 8, 2013, 11:43:04 AM8/8/13
to clojure
Right now I ca't figure why the fn case is working but the anonyous Var is failing because the vars are specially handled:

        else if(value instanceof Var)
            {
            Var var = (Var) value;
            gen.push(var.ns.name.toString());
            gen.push(var.sym.toString());
            gen.invokeStatic(RT_TYPE, Method.getMethod("clojure.lang.Var var(String,String)"));
            }

Christophe

Jamie Brandon

unread,
Aug 8, 2013, 12:30:59 PM8/8/13
to clo...@googlegroups.com
The Fn case works because print-dup is defined on fns:

user=> (binding [*print-dup* true] (println (fn [] nil)))
#=(user$eval2502$fn__2503. )
nil
user=> (read-string "#=(user$eval2502$fn__2503. )")
#<user$eval2502$fn__2503 user$eval2502$fn__2503@19037d90>

I still can't figure out why Compiler.emitValue is used though. Am I
right in thinking that clojure.core/eval is not calling the function
that the compiler uses, but instead serializing everything and pushing
it through the same pathway as compiling text files?

Christophe Grand

unread,
Aug 9, 2013, 4:13:50 AM8/9/13
to clojure
You are right, it's beacsue of print-dup. Hence it works only on functions which aren't closures:

=> (let [a nil x (fn [] a)] (eval [x]))
IllegalArgumentException No matching ctor found for class user$eval3989$x__3990 


Why is emitValue used?
Regular code forms (as returned by the reader) don't usually contain functions. When the compiler doesn't know how to evaluate some value it treats it as a constant. Constants are serialized in the class and read back/instanciated when the class is loaded.

Jamie Brandon

unread,
Aug 9, 2013, 7:29:38 AM8/9/13
to clo...@googlegroups.com
Ah, I found out that java bytecode can't contain data literals so the
compiler has to emit a constructor instead.

Also, since the jvm doesn't gc classes there might be confusing memory
leaks if you could embed a closure in evaluated code.
Reply all
Reply to author
Forward
0 new messages