Fork me on GitHub
#clojure-dev
<
2020-09-07
>
andy.fingerhut01:09:46

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

👋 3
andy.fingerhut01:09:35

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

Alex Miller (Clojure team)13:09:02

The first part about PersistentTreeSet etc is correct. The Vec stuff, not sure

andy.fingerhut01:09:12

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

andy.fingerhut01:09:05

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

andy.fingerhut01:09:18

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.

gfredericks01:09:10

why's the single-quote in front of #bs?

gfredericks01:09:59

This is a good puzzle. The behavior goes back to clojure 1.4 But I can't play with it anymore tonight :(

andy.fingerhut02:09:09

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

hiredman05:09:30

That is the same exception you get if you return a function object from macro that closed over some value

hiredman05:09:46

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

hiredman05:09:05

You should see the same thing with something like (eval (fn [] ~(read-bytes ....)))`

hiredman05:09:42

And the reify that it is throwing on is likely the array manager reify in gvec

hiredman05:09:22

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

gfredericks11:09:03

I couldn't figure out why the array manager would ever try to get printed, especially since the vec object print-dups just fine

gfredericks11:09:30

Also removing the quote makes the error go away

gfredericks11:09:13

Which I guess is probably due to two separate compiler codepaths

andy.fingerhut13:09:49

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

andy.fingerhut13:09:33

Thanks @U0NCTKEV8 for the details. That is helpful, and seems likely the reason why the exception is occurring.

andy.fingerhut14:09:16

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?

andy.fingerhut14:09:46

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'.

gfredericks14:09:21

> eval for such vector returns a PersistentVector ☝️ that could be changed, right?

andy.fingerhut14:09:55

Only by changing Java code in Clojure implementation, I believe?

andy.fingerhut14:09:53

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.

andy.fingerhut14:09:25

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.

andy.fingerhut14:09:50

If Clojure core folks believe eval'ing a primitive vector should return a primitive vector, that would be cool.

andy.fingerhut14:09:59

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.

gfredericks14:09:33

agreed that it requires a java change

gfredericks14:09:59

I was just supposing that it was perhaps the most idealogically-correct solution, though maybe not most-likely-to-be-implemented

andy.fingerhut15:09:10

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.

andy.fingerhut15:09:45

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.

gfredericks12:09:40

but print-dup is presumably the fallback case, and thus gets used for the reify

andy.fingerhut13:09:16

That is correct.

andy.fingerhut13:09:26

I have added some more analysis of the cause of the exception here: https://github.com/jafingerhut/vec-data-reader