Why does `show` for arrays and dictionaries ignore `:compact`?

The documentation for IOContext says

:compact output should not contain line breaks

However, this is ignored by arrays and dictionaries:

julia> show(IOContext(stdout, :compact => true), MIME"text/plain"(), [1, 2])
2-element Vector{Int64}:
 1
 2

julia> show(IOContext(stdout, :compact => true), MIME"text/plain"(), Dict(1 => 2, 3 => 4))
Dict{Int64, Int64} with 2 entries:
  3 => 4
  1 => 2

On the other hand, an array or dictionary inside one of these types is displayed in a compact format:

julia> [[1, 2], Dict(1 => 2, 3 => 4)]
2-element Vector{Any}:
 [1, 2]
 Dict(3 => 4, 1 => 2)

As far as I understand, this would be a typical use case for :compact. This makes it even more surprising that this flag is ignored for these types. Is it for historical reasons? Would it make sense to add that feature?

You are using 3-argument show, which can always be multi-line; the prohibition on line breaks only applies to 2-argument show.

That should be clarified in the docs: clarify that :compact prohibition on line breaks is for 2-arg show by stevengj · Pull Request #55489 · JuliaLang/julia · GitHub

That’s because nested objects are displayed via 2-argument show.

julia> show(IOContext(stdout, :compact => true), [1, 2])
[1, 2]
julia> show(stdout, [1, 2]) # 2-arg show for arrays doesn't use line breaks anyway
[1, 2]
julia> println([1, 2]) # calls show(io, [1, 2]))
[1, 2]

1 Like

Thanks for the clarification. What’s the rationale for restricting the “no line break” rule for :compact to the two-argument show? I understand that the purpose of the three-argument version is to be human-readable. It seems to me that the nesting rule you mention breaks that.

Also, if I implement show for my own type (with unconstrained type parameters), then it’s unclear which other types (from who knows which package) should participate in the nesting rule. Wouldn’t it be cleaner if also the three-argument version of show respects :compact? Then I could simply display every field with :compact, no matter what type it is.

ADDED: One could ask what “compact output” for the three-argument version of show is supposed to mean at all if line breaks are allowed. I would say that avoiding line breaks is the first step to make the output more compact.

From the manual, the whole purpose of 3-argument show from the beginning was for verbose, potentially multi-line display in interactive environments:

[…] 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 […]

Easy: normally, you should use 2-argument show (or functions that call it, like print) for all nested objects. 3-argument show is normally only called at the top level (e.g. by display of a REPL result).

The docstring for show takes a somewhat different point of view:

The representation used by show […] should be parseable Julia code when possible. […] For a more verbose human-readable text output for objects of type T, define show(io::IO, ::MIME"text/plain", ::T) in addition. Checking the :compact IOContext key […] of io in such methods is recommended, since some containers show their elements by calling this method with :compact => true.

From this perspective, it doesn’t feel ideal to switch between the two versions because the parseable and the human-readable form of some element may differ a lot.

I still find the idea appealing that show for arrays and dictionaries pays attention to :compact. For example, the 3-argument show for arrays with :compact could display something in the form of 2-argument show, but with 3-argument show applied to each element. 2-argument show with :compact could leave out the spaces: [1,2,3] instead of [1, 2, 3]. That would make it easier to write show for new containers. Currently it’s more complicated:

According to this description (hopefully not outdated), the 3-argument versions of show for arrays and dictionaries don’t do that. They first call 3-argument show for an element. If the output contains line breaks, they call the 2-argument version. It would be nice to avoid this for every new container type.

1 Like

Yes, I’m not happy with that hack, I think it was a mistake.

I seem to remember that it was added because of some date I/O change, but I’d have to check the git blame.

I will say I don’t like the mixing of parsable vs. pretty display, the two are currently mixed with the 2/3 arg show methods.

This is something I’ve run into when I want to have:

  • a multi-line pretty display
  • an inline pretty display
  • a parsable display

And (2), the “inline pretty display” is hard to have (particularly when printed as a part of other objects) due to the way 2/3 arg show calling is mixed. At this point I almost think it would be better to have different function names for the 2/3 arg show methods to more clearly delineate the “pretty” and “parsable” cases.

5 Likes