Clojure

java object reader constructor doesn't work

Details

  • Type: Defect Defect
  • Status: Open Open
  • Priority: Minor Minor
  • Resolution: Unresolved
  • Affects Version/s: Release 1.4
  • Fix Version/s: None
  • Component/s: None
  • Labels:

Description

Here is a transcript:

;user=> clojure-version
{:major 1, :minor 4, :incremental 0, :qualifier "beta5"}
;user=> (.getProtocol #java.net.URL["file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs"])
java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs, compiling:(null:2)
;user=> (.getProtocol (java.net.URL. "file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs"))
"file"

Another transcript from google groups https://groups.google.com/forum/?fromgroups&hl=en#!topic/clojure/vlsFgVaKcSQ

user=> (def x #java.net.URL["file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs"])
#'user/x
user=> x
#<URL file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs>
user=> (.getProtocol x)
"file"

user=> (.getProtocol #java.net.URL["file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs"])
CompilerException java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs, compiling:(NO_SOURCE_PATH:5)
user=> (defmethod print-dup java.net.URL [o, ^java.io.Writer w] (.write w (str o)))
#<MultiFn clojure.lang.MultiFn@2e694f12>

user=> (.getProtocol #java.net.URL["file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs"])
ClassCastException clojure.lang.Symbol cannot be cast to java.net.URL user/eval11 (NO_SOURCE_FILE:7)
user=>

user=> (def x #java.net.URL["file:///home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs"])
#'user/x
user=> (printf "(class x)=%s x='%s'\n" (class x) x)
(class x)=class java.net.URL x='file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs'
nil
user=> (let [x #java.net.URL["file:///home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs"]]
(printf "(class x)=%s x='%s'\n" (class x) x))
CompilerException java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs, compiling:(NO_SOURCE_PATH:4)

user=> (defmethod print-dup java.net.URL [o, ^java.io.Writer w] (.write w (str o)))
#<MultiFn clojure.lang.MultiFn@3362a63>
user=> (let [x #java.net.URL["file:///home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs"]]
(printf "(class x)=%s x='%s'\n" (class x) x))
(class x)=class clojure.lang.Symbol x='file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs'
nil

Activity

Hide
Brent Millare added a comment -

I think you've explained the underlying problem, so yes. One possible caveat might be that it may be a concern that the current error message is not telling that the underlying problem lies in an improperly initialized object. (or maybe it is, and that's the error message I should expect).

Show
Brent Millare added a comment - I think you've explained the underlying problem, so yes. One possible caveat might be that it may be a concern that the current error message is not telling that the underlying problem lies in an improperly initialized object. (or maybe it is, and that's the error message I should expect).
Hide
Fogus added a comment -

"Fogus, can you please elaborate on using clojure 1.4's tagged literal features."

I can, but it falls outside of the scope of this particular ticket. It might be better to take the broader conversation to the design page at http://dev.clojure.org/display/design/Tagged+Literals and the clojure-dev list.

Has the original motivation for this ticket been addressed?

Show
Fogus added a comment - "Fogus, can you please elaborate on using clojure 1.4's tagged literal features." I can, but it falls outside of the scope of this particular ticket. It might be better to take the broader conversation to the design page at http://dev.clojure.org/display/design/Tagged+Literals and the clojure-dev list. Has the original motivation for this ticket been addressed?
Hide
Brent Millare added a comment -

Fogus, can you please elaborate on using clojure 1.4's tagged literal features. While I understand that you can define data-reader functions, for example

(binding [*data-readers* {'user/f (fn [x] (java.io.File. (first x)))}] (read-string "#user/f [\"hello\"]")) ;=> #<File hello>

however, I feel this is only half a fix, compared with the first mentioned solution (involving print-dup), since clojure's tagged literals only are important for reading, not for printing. Does using tagged literals, so that (read-string (pr-str (read-string ...) works, imply that you must also define print methods per class? If this is true, it seems problematic since if different code wants to define different print methods, this will conflict since defining print-dup methods is global. Is there a good solution for printing objects depending on the context? As an alternative solution, I propose making the default print-method of all objects that didn't already have a printed representation to be a tagged literal, this way, users can customize what it means to read it. (See https://groups.google.com/forum/?fromgroups&hl=en#!topic/clojure/GdT5cO6JoSQ )

Show
Brent Millare added a comment - Fogus, can you please elaborate on using clojure 1.4's tagged literal features. While I understand that you can define data-reader functions, for example
(binding [*data-readers* {'user/f (fn [x] (java.io.File. (first x)))}] (read-string "#user/f [\"hello\"]")) ;=> #<File hello>
however, I feel this is only half a fix, compared with the first mentioned solution (involving print-dup), since clojure's tagged literals only are important for reading, not for printing. Does using tagged literals, so that (read-string (pr-str (read-string ...) works, imply that you must also define print methods per class? If this is true, it seems problematic since if different code wants to define different print methods, this will conflict since defining print-dup methods is global. Is there a good solution for printing objects depending on the context? As an alternative solution, I propose making the default print-method of all objects that didn't already have a printed representation to be a tagged literal, this way, users can customize what it means to read it. (See https://groups.google.com/forum/?fromgroups&hl=en#!topic/clojure/GdT5cO6JoSQ )
Hide
Fogus added a comment -

What would it mean to construct an arbitrary Java object for the purpose of embedding it in code in a generic way? You could say that it's just a matter of calling its constructor with the right args but very often in Java that is not enough to make an object considered "initialize". Sometimes there are init methods or putters or whatever that are required for object construction. So right there I hope it's clear that even though #some.Klass["foo"] provides a way to call an arbitrary constructor, it's in no way a guarantee that an instance is properly constructed. The reason that (for example) defrecords are embeddable anywhere is because we know for certain that the constructor creates a fully initialized instance. If you need to embed specific instances in your own code then you have two options:

  • Implement a print-dup for the class that guarantees a fully initialized object is built.
  • Use Clojure 1.4's tagged literal feature to do the same.

Quick point of note:

Your code

(defmethod print-dup java.net.URL [o, ^java.io.Writer w] (.write w (str o)))

Doesn't do what you think it does. It spits out exactly file:///home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs that Clojure reads in as a symbol. Something like the following might be more appropriate:

(defmethod print-dup java.net.URL [o, ^java.io.Writer w] (.write w "#java.net.URL") (.write w (str [ (str o) ])))

(let [x #java.net.URL["file:///home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs"]]                             
  (printf "(class x)=%s x='%s'\n" (class x) x) x)                                                                          

; (class x)=class java.net.URL x='file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs'

;=> #<URL file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs>

(.getProtocol *1)                        
;=> "file"
Show
Fogus added a comment - What would it mean to construct an arbitrary Java object for the purpose of embedding it in code in a generic way? You could say that it's just a matter of calling its constructor with the right args but very often in Java that is not enough to make an object considered "initialize". Sometimes there are init methods or putters or whatever that are required for object construction. So right there I hope it's clear that even though #some.Klass["foo"] provides a way to call an arbitrary constructor, it's in no way a guarantee that an instance is properly constructed. The reason that (for example) defrecords are embeddable anywhere is because we know for certain that the constructor creates a fully initialized instance. If you need to embed specific instances in your own code then you have two options:
  • Implement a print-dup for the class that guarantees a fully initialized object is built.
  • Use Clojure 1.4's tagged literal feature to do the same.
Quick point of note: Your code
(defmethod print-dup java.net.URL [o, ^java.io.Writer w] (.write w (str o)))
Doesn't do what you think it does. It spits out exactly file:///home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs that Clojure reads in as a symbol. Something like the following might be more appropriate:
(defmethod print-dup java.net.URL [o, ^java.io.Writer w] (.write w "#java.net.URL") (.write w (str [ (str o) ])))

(let [x #java.net.URL["file:///home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs"]]                             
  (printf "(class x)=%s x='%s'\n" (class x) x) x)                                                                          

; (class x)=class java.net.URL x='file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs'

;=> #<URL file:/home/hara/dj/usr/src/clojurescript/src/cljs/cljs/core.cljs>

(.getProtocol *1)                        
;=> "file"
Hide
Andy Fingerhut added a comment -

I've confirmed this behavior with 1.4.0 beta5. I've only tracked it down as far as finding the "Can't embed object in code" message, which is easy to find in Compiler.java in method emitValue. That exception is thrown because printString in RT.java throws an exception.

It isn't clear to me what should be done instead, though.

Show
Andy Fingerhut added a comment - I've confirmed this behavior with 1.4.0 beta5. I've only tracked it down as far as finding the "Can't embed object in code" message, which is easy to find in Compiler.java in method emitValue. That exception is thrown because printString in RT.java throws an exception. It isn't clear to me what should be done instead, though.

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated: