Open stevengj opened 8 years ago
We did it that way originally; the attribute was called multiline
. Was changed by #17113
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
Okay, maybe it just needs to be documented, then?
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 ofshow
that takes the text/plain MIME type as its second argument (see Multimedia I/O). Technically, the REPL callsdisplay(z)
to display the result of executing a line, which defaults toshow(stdout, MIME("text/plain"), z)
, which in turn defaults toshow(stdout, z)
, but you should not define newdisplay
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.
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.
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).
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.
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?
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/>).
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.
Correct; there is no such thing as requesting multi-line output. What we have instead is methods with a MIME type, and those without.
The manual states that since the default
show(io, "text/plain", x)
calls through toshow(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 callsshow(io, "text/plain", x)
, notshow(io, x)
, and the array display exploits this to output a "2d" representation of a matrix in the REPL by callingshowarray(io, x, false)
fromshow(io, "text/plain", x)
, whereas it separately overloadsshow(io, x)
to callshowarray(io, x, true)
and get a "1d" representation of a matrix forprint
and friends.This trick is not documented, and seems contrary to the spirit of #14052. Maybe
display
should callshow(IOContext(io, someattribute=true), "text/plain", x)
for some attribute that indicates that a "display" version ofx
is desired?cc @vtjnash