JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.73k stars 5.49k forks source link

show(io, x) vs show(io, "text/plain", x) -- docs? #18004

Open stevengj opened 8 years ago

stevengj commented 8 years ago

The manual states that since the default show(io, "text/plain", x) calls through to show(io, x), all you ever have to define is the latter. However, this doesn't seem to be quite true.

In particular, the REPL calls display(x) which calls show(io, "text/plain", x), not show(io, x), and the array display exploits this to output a "2d" representation of a matrix in the REPL by calling showarray(io, x, false) from show(io, "text/plain", x), whereas it separately overloads show(io, x) to call showarray(io, x, true) and get a "1d" representation of a matrix for print and friends.

This trick is not documented, and seems contrary to the spirit of #14052. Maybe display should call show(IOContext(io, someattribute=true), "text/plain", x) for some attribute that indicates that a "display" version of x is desired?

cc @vtjnash

JeffBezanson commented 8 years ago

We did it that way originally; the attribute was called multiline. Was changed by #17113

Evizero commented 8 years ago

aha! I did run into this a couple of times and was kinda confused what I was doing wrong (I am confused easily). Not complaining here though, never was a serious issue for what I was trying to do. Just confirming that some people do indeed run into this in practice

stevengj commented 8 years ago

Okay, maybe it just needs to be documented, then?

nalimilan commented 6 years ago

The situation has changed a bit since this issue was filed, but the problem remains. What has changed is that we now have a fallback:

show(io::IO, ::MIME"text/plain", x) = show(io, x)

Yet, the manual still recommends overloading the two-argument show(io, x) method for single-line printing, but the three-argument show(stdout, MIME("text/plain"), x) method for multiline printing:

sometimes one wants both a verbose multi-line printing format, used for displaying a single object in the REPL and other interactive environments, and also a more compact single-line format used for print or for displaying the object as part of another object (e.g. in an array). Although by default the show(io, z) function is called in both cases, you can define a different multi-line format for displaying an object by overloading a three-argument form of show that takes the text/plain MIME type as its second argument (see Multimedia I/O). Technically, the REPL calls display(z) to display the result of executing a line, which defaults to show(stdout, MIME("text/plain"), z), which in turn defaults to show(stdout, z), but you should not define new display methods unless you are defining a new multimedia display handler (see Multimedia I/O).

I find this approach confusing given that it parallels the IOContext system which is used to control other aspects of printing. To make this more systematic, I suggest we reintroduce a :multiline IOContext property which can be checked just like other properties in any show method, be it for text/plain or for any other format.

The argument against this which justified the removal of the :multiline property in https://github.com/JuliaLang/julia/pull/17113 was that it doesn't nest automatically: an array will use multi-line printing, but its elements should each take a single line. But I don't see the problem: containers can just pass :multiline => false when printing their elements, just like they typically pass :compact => true and typeinfo => eltype(c). Non-container types (the majority) don't have to bother. Yes, it will require some adjustments to set :multiline => false in places where we could just call show(io, x), but that doesn't seem to be a big problem.

I think that using an IOContext property is more compelling now that we have cleaned up the printing machinery and deprecated showcompact in favor of it (https://github.com/JuliaLang/julia/pull/26080). Now can potentially simplify it around only two concepts: the display MIME format, and IOContext properties. That also allows choosing single-line or multi-line display for formats other than text/plain, which is currently impossible. At the time when #17113 was merged, we didn't have a completely clear system so it made sense to apply a practical fix to avoid regressions. We can probably do better now.

stevengj commented 6 years ago

Almost all user types are containers for other types. This means that multiline=false will potentially have to be set in a huge number of places.

In fact, arguably all calls to print (which currently calls the 2-argument show) should set multiline to false, which would mean that every print call would introduce the overhead of constructing an IOContext.

vtjnash commented 6 years ago

Correct, that is why :multiline was a bit of a mess. It wasn't obvious though until we had tried it for a few months. I try to explain it as a "text/plain" formatted document being different from string formatting – which isn't quite obvious, but the difference being also that layout can only be applied at the outer-most level & shouldn't try to apply recursively. This is precisely what MIME means, while IOContext means parameters that are valid for all recursive consumers to use – or ignore (it's never correct to expect to override them).

JeffBezanson commented 6 years ago

I would also prefer not to flip-flop on this. In practice, this would have the effect of expanding 2 forms of show to 4, which will not be as much of a net improvement as we'd like.

nalimilan commented 6 years ago

OK. So that means we consider that the multi-line/single-line distinction doesn't make sense for formats other than plain text? Or maybe the difference between the two-argument and the three-argument show methods isn't single vs. multiple lines, but rather "Julia syntax-like" vs. custom pretty output?

vtjnash commented 6 years ago

No, there's nothing particularly special about plain-text output, other than (like the "text/plain" mime type) being the default (and, specifically, to output utf8-encoded content). I created an example of an HTML stream (text/html mime type) as part of my research during creation of the IOContext concept (https://github.com/JuliaLang/julia/compare/jn/iocolor#diff-c20eb2eead14027fab4293e10bacf73bR398). The difference as actually more obvious with complex formats, where two-arg show renders only a portion of a document (such as some HTML formatting tags), while three-arg show is expected to render a completed document (such as adding \<head/>\<body/>).

nalimilan commented 6 years ago

Sorry, I don't really understand your example. What I meant is that there's no way to request printing a custom type using a single or multiple lines in the text/html output format since there's no IOContext property for that. Since we use the distinction between the two-argument and three-argument methods to decide whether to use multiple lines or not, all formats other than the two-argument default can only be multiline.

JeffBezanson commented 6 years ago

Correct; there is no such thing as requesting multi-line output. What we have instead is methods with a MIME type, and those without.

nalimilan commented 6 years ago

Then can we find better guidelines regarding what kind of output the method without a MIME type should produce compared to those with a MIME type? As I noted above, the manual still mentions multi-line output as a difference.