Classes like PersistentTreeSet PersistentTreeMap PersistentVector all have create
methods, and the default print-dup
method for objects of some of those types generate #=
strings that contain calls to the create
methods of those classes, like so:
user=> (binding [*print-dup* true] (print (sorted-set 3 2 1)))
#=(clojure.lang.PersistentTreeSet/create [1 2 3])nil
This also happens when printing a primitive vector, except as far as I can tell, class clojure.core.Vec has no create
method, and classes created with deftype
(as clojure.core.Vec
is) cannot have static methods. Does that sound correct?
user=> (binding [*print-dup* true] (print (vector-of :byte 1 2 3)))
#=(clojure.core.Vec/create [#=(java.lang.Byte. "1") #=(java.lang.Byte. "2") #=(java.lang.Byte. "3")])nil
The first part about PersistentTreeSet etc is correct. The Vec stuff, not sure
On a perhaps only tangentially related note (in the sense that I discovered both behaviors while investigating if it is possible to make tagged literals for (vector-of :byte 1 2 3)
), I am having trouble figuring out why the following exception occurs:
$ cat deps.edn
{:deps {org.clojure/clojure {:mvn/version "1.10.1"}} :paths ["."]}
$ cat data_readers.clj
{bs user/read-bytes}
$ clojure -e "(defn read-bytes [bs] (apply vector-of :byte bs)) (class '#bs [-1 0 1])"
#'user/read-bytes
Syntax error compiling fn* at (REPL:1:51).
Can't embed object in code, maybe print-dup not defined: clojure.core$reify__8311@6ccdb29f
Full report at:
/var/folders/rv/2vbzx7zj64x4hss7rdl1xywc0000gn/T/clojure-8633821538019017760.edn
I couldn't figure out why the array manager would ever try to get printed, especially since the vec object print-dups just fine
Also removing the quote makes the error go away
Which I guess is probably due to two separate compiler codepaths
Removing the quote probably leads to a different non-ideal behavior, which is that the primitive vector is eval'd, and eval for such vector returns a PersistentVector. The quote was my experimental attempt to keep the primitive vector
Thanks @hiredman for the details. That is helpful, and seems likely the reason why the exception is occurring.
I guess there isn't really a way without modifying Java source code for Clojure's implementation to cause the compiler to use the print-dup
method of a deftype
type?
I am not sure if it were possible, whether it would 'solve the problem', as the problem is only vaguely defined as 'it might be cool if it were easier to have tagged literals for primitive byte vectors that could be conveniently used in Clojure source code'.
> eval for such vector returns a PersistentVector ☝️ that could be changed, right?
Only by changing Java code in Clojure implementation, I believe?
And not clear to me yet whether it would avoid the exception when trying to use a vector of bytes in-line in source code.
I'm currently in a situation where it isn't clear to me whether the output of print-dup ought to be used for in-line occurrences of data structures, or at least for which subset of JVM objects it should, and which it should not.
If Clojure core folks believe eval'ing a primitive vector should return a primitive vector, that would be cool.
or at least I am not aware of any reasons why such a change would lead to problems, but given my level of understanding of the interdependencies of behavior, my 'that would be cool' statement above betrays 17 levels of ignorance.
agreed that it requires a java change
I was just supposing that it was perhaps the most idealogically-correct solution, though maybe not most-likely-to-be-implemented
In a few further experiments, it seems that print-dup behavior is pretty much independent of the reason for the exception -- the Compiler class code for emitValue
does not seem to use print-dup in any way I can see, at least not for objects whose types are created by deftype
.
For objects with types created via deftype
, it will currently always try to do emitValue
on every field of the object. The values of those fields might use print-dup
behavior defined for them, but that depends upon what the class of those fields happens to be.
but print-dup
is presumably the fallback case, and thus gets used for the reify
That is correct.
I have added some more analysis of the cause of the exception here: https://github.com/jafingerhut/vec-data-reader
More details on the stack backtrace in a thread, in case people are interested:
$ clojure
Clojure 1.10.1
user=> (defn read-bytes [bs] (apply vector-of :byte bs))
#'user/read-bytes
user=> (class '#bs [-1 0 1])
Syntax error compiling fn* at (REPL:1:1).
Can't embed object in code, maybe print-dup not defined: clojure.core$reify__8311@5f7b97da
user=> (pst)
Note: The following stack trace applies to the reader or compiler, your code was not executed.
CompilerException Syntax error compiling fn* at (1:1). #:clojure.error{:phase :compile-syntax-check, :line 1, :column 1, :source "NO_SOURCE_PATH", :symbol fn*}
clojure.lang.Compiler.analyzeSeq (Compiler.java:7115)
clojure.lang.Compiler.analyze (Compiler.java:6789)
clojure.lang.Compiler.eval (Compiler.java:7174)
clojure.lang.Compiler.eval (Compiler.java:7132)
clojure.core/eval (core.clj:3214)
clojure.core/eval (core.clj:3210)
clojure.main/repl/read-eval-print--9086/fn--9089 (main.clj:437)
clojure.main/repl/read-eval-print--9086 (main.clj:437)
clojure.main/repl/fn--9095 (main.clj:458)
clojure.main/repl (main.clj:458)
clojure.main/repl-opt (main.clj:522)
clojure.main/main (main.clj:667)
Caused by:
RuntimeException Can't embed object in code, maybe print-dup not defined: clojure.core$reify__8311@5f7b97da
clojure.lang.Util.runtimeException (Util.java:221)
clojure.lang.Compiler$ObjExpr.emitValue (Compiler.java:4893)
clojure.lang.Compiler$ObjExpr.emitValue (Compiler.java:4808)
clojure.lang.Compiler$ObjExpr.emitConstants (Compiler.java:4934)
clojure.lang.Compiler$ObjExpr.compile (Compiler.java:4612)
clojure.lang.Compiler$FnExpr.parse (Compiler.java:4106)
clojure.lang.Compiler.analyzeSeq (Compiler.java:7105)
clojure.lang.Compiler.analyze (Compiler.java:6789)
nil
Defining a print-dup
for class clojure.core.Vec
that prints out a string like #user/bs [1 2 3]
causes the same exception, so however that ...reify...
object is involved in the exception seems independent of print-dup
behavior of class clojure.core.Vec
,but I am not certain about that.
why's the single-quote in front of #bs
?
This is a good puzzle. The behavior goes back to clojure 1.4 But I can't play with it anymore tonight :(
Yeah, no urgency on this, more of a puzzle I am curious about and may spend a little time on looking for answers. Inspired by this question here: https://ask.clojure.org/index.php/9567/possible-create-tagged-literal-reader-for-clojure-core-vec
That is the same exception you get if you return a function object from macro that closed over some value
For pretty much the same reason, you have a reify that closes over some value (from the gvec implementation) being passed to the evaluator, and it doesn't know how to embed that in byte code
You should see the same thing with something like (eval
(fn [] ~(read-bytes ....)))`
And the reify that it is throwing on is likely the array manager reify in gvec
https://github.com/clojure/clojure/blob/master/src/clj/clojure/gvec.clj#L458
Actually the exception is a little different from the closed over fn case, but I think that is till correct. Deftypes actually have special handling in the compiler if I recall for being constructed into byte code, so the generic print-dup path is never hit for gvec