Hi Atamert,

I'm not an expert but I believe REPL does compile the forms you enter to 
> bytecode. It compiles an fn form into a JVM class that implements IFn. It 
> also compiles a quoted form (such as `(fn ...) ) but this time it takes the 
> form of a list (as in (list ...) ), IOW it's still data after it is 
> compiled. The latter is executed directly and the former is evaluated 
> (turned into bytecode and then executed perhaps).
>

So I tried what I probably should have tried in the first place and used 
the debugger.
I think I understand what's going on now.

user=> [(Object.)]
works: [#<Object@...>]

user=> (eval `[ ~(Object.) ])
doesn't: CompilerException java.lang.RuntimeException: Can't embed object 
in code, maybe print-dup not defined

It begins by calling clojure.lang.Compiler.analyze on the list
(fn* [] (eval (clojure.core/apply clojure.core/vector (clojure.core/seq 
(clojure.core/concat (clojure.core/list (Object.)))))))
Recursively going through the expression and building up a tree of 
ConstantExpr, ending up with a
Compiler$FNExpr, that is actually compiled to Bytecode, it calls .eval() on 
this which returns an IFn (which is an instance of the dynamically-created 
class) and calls .invoke() on that.
We end up with an FNExpr with src= the list (that inner vector holds an 
actual reference to the Object, no strings anywhere)
(fn* [] [#<Object java.lang.Object@4b2e26cf>])
The error occurs when compiling this to Bytecode, specifically in:
emitValue( value = #<Object java.lang.Object@4b2e26cf> ... )
That method explicitly handles emitting bytecode that sits in the static 
constructor of the IFn for null and constants of type Boolean etc ... ISeq, 
IPersistentList and Pattern.
For all other types it calls RT.printString(value), trying to emit that 
string as a constant, to be interpreted by readString.
As the error message says printString fails for Object.

So it's not at all like I suspected:
- the unquoting doesn't clone anything, it simply the value in the list.
- eval doesn't roundtrip the entire form to String and back.

Eval clones everything because you can't embed a pointer to an existing 
object as a constant in Bytecode.
The reason the closure doesn't need to do element-wise comparison in my 
example is because the pointer to the existing object is passed as a 
constructor parameter at runtime.
f4 is so much slower than f1 despite apparently having the same bytecode is 
that the constructor for f1 is called with a pointer to the original 
object, while the constructor to f4 is called inside the eval, with a 
pointer to a clone of the object.

As far as I can tell AOT goes through the same steps and then just saves 
the generated bytecode in a .class file.
There appears to never be any kind of "interpretation", it's always 
generating bytecode and having the JVM evaluate that, even for the most 
trivial expressions like (inc 1).

Sorry for spreading my confusion about this; probably would have been 
better suited as a blog post.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@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+unsubscr...@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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to