Get back human-read-able objects in the REPL (*ns*, *e, etc.)

299 views
Skip to first unread message

Laurent PETIT

unread,
Jul 23, 2015, 10:16:07 AM7/23/15
to cloju...@googlegroups.com
Hello,

Could we use the existing `clojure.core/*print-readable*` dyn var to fix the `clojure.core/print-object` function so that it honors the dyn var and prints objects in a more human-read-able way (the old behavior in Clojure 1.6) ?

Currently, the hotfix looks like this:

(alter-var-root #'clojure.core/print-object
  (fn [old-print-object]
    (fn [o, ^java.io.Writer w]
      (if *print-readably*
       (old-print-object o w)
       (do
         (when (instance? clojure.lang.IMeta o)
           (#'clojure.core/print-meta o w))
         (.write w "#<")
         (let [name (.getSimpleName (class o))]
           (when (seq name) ;; anonymous classes have a simple name of ""
             (.write w name)
             (.write w " ")))
         (.write w (str o))
         (.write w ">"))))))

So the patch would be quite simple.


--
Laurent Petit

Laurent PETIT

unread,
Jul 23, 2015, 12:28:01 PM7/23/15
to cloju...@googlegroups.com
Of course, that cannot be achieved via `*print-readably*`, my bad.

Some additional dynamic var should be created for this purpose, e.g. `*print-in-repl*`, and it would be automatically set to true by the repl.
--
Laurent Petit

Stuart Halloway

unread,
Jul 29, 2015, 9:42:51 AM7/29/15
to cloju...@googlegroups.com
Hi Laurent,

Can you rephrase this as a problem statement, with examples?

Thanks,
Stu

--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure-dev...@googlegroups.com.
To post to this group, send email to cloju...@googlegroups.com.
Visit this group at http://groups.google.com/group/clojure-dev.
For more options, visit https://groups.google.com/d/optout.

Laurent PETIT

unread,
Jul 29, 2015, 4:23:41 PM7/29/15
to cloju...@googlegroups.com
Hi Stu, sure:

With Clojure 1.6, switching namespaces in a REPL results in the following output to the console:

1.6.0 % java -jar clojure-1.6.0.jar
Clojure 1.6.0
user=> (in-ns 'clojure.core)
#<Namespace clojure.core>
clojure.core=>

With Clojure 1.7, this looks like this:

1.7.0 % java -jar clojure-1.7.0.jar
Clojure 1.7.0
user=> (in-ns 'clojure.core)
#object[clojure.lang.Namespace 0x5efa40fe "clojure.core"]
clojure.core=>

Primary REPL users are humans, and the new way of printing arbitrary objects, #object[clojure.lang.Namespace 0x5efa40fe "clojure.core"], instead of #<Namespace clojure.core>, has been reported by users as a REPL regression. It's harder to decipher. The interesting information is at the far right, and there's more visual clutter.


A similar issue occurs if you want to recall the last throw exception:

With Clojure 1.6.0:

user=> *e
#<RuntimeException java.lang.RuntimeException: test>
user=>

With Clojure 1.7.0:
clojure.core=> *e
#error {
 :cause "root"
 :via
 [{:type java.lang.RuntimeException
   :message "test"
   :at [clojure.core$eval3 invoke "NO_SOURCE_FILE" 5]}
  {:type java.lang.RuntimeException
   :message "root"
   :at [clojure.core$eval3 invoke "NO_SOURCE_FILE" 5]}]
 :trace
 [[clojure.core$eval3 invoke "NO_SOURCE_FILE" 5]
  [clojure.lang.Compiler eval "Compiler.java" 6782]
  [clojure.lang.Compiler eval "Compiler.java" 6745]
  [clojure.core$eval invoke "core.clj" 3081]
  [clojure.main$repl$read_eval_print__7099$fn__7102 invoke "main.clj" 240]
  [clojure.main$repl$read_eval_print__7099 invoke "main.clj" 240]
...  [cut] ....

The interesting information starts at lines 4 and 5, and in real life scenario one must probably scroll up to find them at the very top of the stacktrace dump.

So, having that as the out of the box experience feels also like a REPL regression to me.

The patch in my first email intends to revert back to the 1.6 behavior for printing arbitrary java objects and exceptions, *only when printed for the REPL*, e.g. for consumption by a human.
--
Laurent Petit

Nelson Morris

unread,
Jul 29, 2015, 7:24:35 PM7/29/15
to cloju...@googlegroups.com
Chas and Alex had a similar conversation in irc about test.check and the new exception printing: http://clojure-log.n01se.net/date/2015-05-19.html#10:59

Laurent PETIT

unread,
Jul 29, 2015, 7:42:02 PM7/29/15
to cloju...@googlegroups.com
Yes, thanks for mentioning this, Nelson.

The fix I suggested is an improvement over Chas' own suggestion in a gist (I have just added the dyn var-based conditional to restrict the use of the 1.6-version of print-object to only REPL usage)

Bozhidar Batsov

unread,
Jul 30, 2015, 6:56:14 AM7/30/15
to cloju...@googlegroups.com
Add my voice to the "this feels like a regression" camp.

Stuart Halloway

unread,
Jul 30, 2015, 4:04:15 PM7/30/15
to cloju...@googlegroups.com
Thanks Laurent, I get it now.

"Humanize at the edge" seems fine, but I am unconvinced by the argument/presumption that "at the REPL"  really means "at the edge".

Daniel Compton

unread,
Aug 2, 2015, 12:37:02 AM8/2/15
to cloju...@googlegroups.com
Hi Stuart


> "Humanize at the edge" seems fine, but I am unconvinced by the argument/presumption that "at the REPL"  really means "at the edge".

Can you explain what you mean by this? As far as I know, there aren't vast swaths of bot armies also running Clojure REPLs that we need to cater for? :) I feel like I'm missing the point you were trying to get across here.

--
Daniel
--
--
Daniel

Alex Miller

unread,
Aug 11, 2015, 4:25:21 PM8/11/15
to Clojure Dev
Kind of just picking this stale thread up in the middle, if you want to revert to pre-1.7 repl printing behavior, you can drop this in your user.clj:

(defn print-obj [o ^java.io.Writer w] (.write w (str "#<" (.. o getClass getSimpleName) " " (.toString o) ">")))
(defmethod print-method Object [o ^java.io.Writer w] (print-obj o w))
(defmethod print-method Throwable [o ^java.io.Writer w] (print-obj o w))

or run it at the repl.

Bozhidar Batsov

unread,
Aug 12, 2015, 1:40:31 AM8/12/15
to cloju...@googlegroups.com
Alex, that's great, but I think some explanations why this change was done in the first place would be pretty useful. As Daniel said - REPLs are usually used by humans and optimizing for human readability there sounds pretty reasonable to me. 

--

Alex Miller

unread,
Aug 12, 2015, 8:54:31 AM8/12/15
to cloju...@googlegroups.com
In addition to being programs that communicate with people, repls are also programs that communicate with other programs. It is useful if the data exchanged between these programs is read-able (Clojure data that can be read by the reader). 

Alex Miller

unread,
Aug 12, 2015, 8:57:54 AM8/12/15
to Clojure Dev
I will also point you at Rich's comments in a prior thread:

Laurent PETIT

unread,
Aug 12, 2015, 9:28:35 AM8/12/15
to cloju...@googlegroups.com
2015-08-12 14:57 GMT+02:00 Alex Miller <al...@puredanger.com>:
I will also point you at Rich's comments in a prior thread:

Thanks Alex for bringint it back. Here's the quote that interests me:

"
But we have improved Clojure in this area - now the default print-method prints something that is read-able (with an appropriate tag handler installed). Another area where many REPLs presume a human consumer and print un-read-ably is when exceptions occur. This socket REPL will default to printing exceptions read-ably, but you can switch to a human oriented formatter of your choice. 
"

Questions:
- is there a switch for printing exceptions human oriented? I don't see it atm. I feel like globally redef'ing print-object, print-method for Object and Throwable is gross. It might break user programs (or libraries they use) which may be written with the assumption that everything is read-able. We need a dynamic switch, which can be bound from the REPL, I think
- while the quote does not explicitly mention a switch for objects, I would argue that the same switch as above could serve the purpose

 


On Wednesday, August 12, 2015 at 7:54:31 AM UTC-5, Alex Miller wrote:
In addition to being programs that communicate with people, repls are also programs that communicate with other programs. It is useful if the data exchanged between these programs is read-able (Clojure data that can be read by the reader). 

On Wed, Aug 12, 2015 at 12:40 AM, Bozhidar Batsov <bozh...@batsov.com> wrote:
Alex, that's great, but I think some explanations why this change was done in the first place would be pretty useful. As Daniel said - REPLs are usually used by humans and optimizing for human readability there sounds pretty reasonable to me. 

--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure-dev...@googlegroups.com.
To post to this group, send email to cloju...@googlegroups.com.
Visit this group at http://groups.google.com/group/clojure-dev.
For more options, visit https://groups.google.com/d/optout.



--
Laurent Petit

Christophe Grand

unread,
Aug 13, 2015, 12:38:20 AM8/13/15
to clojure-dev
On Wed, Aug 12, 2015 at 8:28 AM, Laurent PETIT <lauren...@gmail.com> wrote:
- is there a switch for printing exceptions human oriented? I don't see it atm. I feel like globally redef'ing print-object, print-method for Object and Throwable is gross. It might break user programs (or libraries they use) which may be written with the assumption that everything is read-able. We need a dynamic switch, which can be bound from the REPL, I think
- while the quote does not explicitly mention a switch for objects, I would argue that the same switch as above could serve the purpose

Isn't the intent of these change that Clojure does not have to expose more switches?
You get back a readable form from the repl, it's up to ccw (for example) to read it and display it in a user-friendly manner.

Christophe

--
On Clojure http://clj-me.cgrand.net/
Clojure Programming http://clojurebook.com
Training, Consulting & Contracting http://lambdanext.eu/

Laurent PETIT

unread,
Aug 13, 2015, 4:36:18 AM8/13/15
to cloju...@googlegroups.com


Le jeudi 13 août 2015, Christophe Grand <chris...@cgrand.net> a écrit :
On Wed, Aug 12, 2015 at 8:28 AM, Laurent PETIT <lauren...@gmail.com> wrote:
- is there a switch for printing exceptions human oriented? I don't see it atm. I feel like globally redef'ing print-object, print-method for Object and Throwable is gross. It might break user programs (or libraries they use) which may be written with the assumption that everything is read-able. We need a dynamic switch, which can be bound from the REPL, I think
- while the quote does not explicitly mention a switch for objects, I would argue that the same switch as above could serve the purpose

Isn't the intent of these change that Clojure does not have to expose more switches?
You get back a readable form from the repl, it's up to ccw (for example) to read it and display it in a user-friendly manner.

In theory. But since the repl outpustream is shared by what's printed directly by the P of the REPL and whatever code evaluation has been launched by the user command potentially during evaluation or in background thread, it's hard to hope that whatever is read from the repl input is a readable as long as we don't have full control (eg dedicated ccw repl) over its use.
We're talking about the repl the user is playing with, and we cannot assume much about what comes in/out without risking to limit some of its use (referring here to the examples given by Rich). So it's better if the repl itself, out of the box or through bindable dynamic var (the way repls are dynamically configurable) takes care of that on the "server-side"

Christophe

--
On Clojure http://clj-me.cgrand.net/
Clojure Programming http://clojurebook.com
Training, Consulting & Contracting http://lambdanext.eu/

--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure-dev...@googlegroups.com.
To post to this group, send email to cloju...@googlegroups.com.
Visit this group at http://groups.google.com/group/clojure-dev.
For more options, visit https://groups.google.com/d/optout.


--
Laurent Petit

Reply all
Reply to author
Forward
0 new messages