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.