Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Printing of time-related types in containers is inconsistent #30901

Closed
tkf opened this issue Jan 30, 2019 · 66 comments
Closed

Printing of time-related types in containers is inconsistent #30901

tkf opened this issue Jan 30, 2019 · 66 comments
Labels
dates Dates, times, and the Dates stdlib module display and printing Aesthetics and correctness of printed representations of objects.
Milestone

Comments

@tkf
Copy link
Member

tkf commented Jan 30, 2019

I think #30817 changed show(io, "text/plain", ::Array{Day}) etc. On cb7a569 (and in Julia 1.0 and 1.1) it was:

julia> Day.(1:3)
3-element Array{Day,1}:
 1 day
 2 days
 3 days

But now (c0e9182) it's:

julia> Day.(1:3)
3-element Array{Day,1}:
 Day(1)
 Day(2)
 Day(3)

Furthermore, confusingly (to me), Matrix{Day} still prints human readable form:

julia> [Day(1) Day(2)]
1×2 Array{Day,2}:
 1 day  2 days

However, @omus and others approved the following behavior in #30200:

julia> fill(d, 2)
2-element Array{Date,1}:
 Date(2018, 12, 7)
 Date(2018, 12, 7)

julia> fill(d, 2, 2)
2×2 Array{Date,2}:
 2018-12-07  2018-12-07
 2018-12-07  2018-12-07

So maybe the current behavior of Vector{Day} and Matrix{Day} is intentional? (But, to be honest, I was confused why ndims of an array changes how elements are printed.)

Another confusing part is that show(io, ::Array{Day}) still shows human-readable form. Shouldn't it closer to repr than 3-argument show?

julia> show(Day.(1:2))
Day[1 day, 2 days]

Note that the output of Dict{Day,Day} and Vector{Pair{Day,Day}} are not changed. It has been (and is):

julia> Dict(Day(1) => Day(2))
Dict{Day,Day} with 1 entry:
  1 day => 2 days

julia> [Day(1) => Day(2)]
1-element Array{Pair{Day,Day},1}:
 1 day => 2 days

What is the intended printing behavior of Day, Date, and alike in the containers?

See also: #30683

@nalimilan
Copy link
Member

Matrix printing sets :compact=>true, vector printing doesn't. And indeed since #30817 show checks for that property to decide the printing format. That's doesn't make a lot of sense to me since both representations take the same number of characters. What @JeffBezanson suggested in that PR was to change repr to use Day(1), but still print 1 day from show, but that's not what's been implemented.

What we could also do is check the :typeinfo property of IOContext, and if it's equal to Day, print only the number, without the mention of the unit. That would give a super compact printing in most arrays and in data frames.

Cc: @fchorney

@nalimilan nalimilan added dates Dates, times, and the Dates stdlib module display and printing Aesthetics and correctness of printed representations of objects. labels Jan 30, 2019
@JeffBezanson
Copy link
Member

We should remove the branch that switches to 1 day based on the :compact property. We can optionally add typeinfo support as well.

@nalimilan
Copy link
Member

@tkf Would you make a pull request?

@tkf
Copy link
Member Author

tkf commented Jan 30, 2019

I think I can make a PR, but I need to understand the desired output.

If we check :typeinfo and only print a number, show(Day.(1:2)) would prints Day[1, 2], right? But this can't be evaluated now because Base.convert(::Type{Day}, ::Int) is not defined. Should we still print Day[1, 2] here?

Branching on :compact seems to be added to show human-readable format in DataFrame #30200 (comment). But my guess is that the fix should go into DataFrames.jl. It seems that DataFrames.jl is using 2-arg show to pretty-print the element: https://github.com/JuliaData/DataFrames.jl/blob/8d75d7464bdea76bb394081f66d870b2d9bf1c3b/src/abstractdataframe/show.jl#L20-L30 I suppose it should be using show(IOContext(io, :compact=>true, :typeinfo=>typeof(x)), "text/plain", x). So, is it OK to temporary break DataFrame printing?

Likewise, Base.print_matrix_row also uses 2-arg show for elements even though print_matrix_row is called from 3-arg show for an array.

sx = sprint(show, x, context=io, sizehint=0)

It seems to me that it should be using 3-arg show with :compact => true. This also applies to other container types like Dict.

(All my above comments are based on the assumption that changing show is considered a "minor change".)

@nalimilan
Copy link
Member

I guess we can defer the :typeinfo change for now, since convert(::Type{Day}, ::Int) may be controversial. Though I'm not sure whether the output of show is always supposed to be parseable (I guess @JeffBezanson knows?).

Regarding the two-argument vs. three-argument show, AFAIK arrays should use the two-argument method, as the three-argument one is for multiline printing.

@tkf
Copy link
Member Author

tkf commented Jan 31, 2019

Though I'm not sure whether the output of show is always supposed to be parseable (I guess @JeffBezanson knows?).

I already asked him this in #30757. His answer (#30757 (comment)) was:

I should add that we are not really strict about show output needing to be parseable. I think the reason for that is that I don't really see many use cases for parseable output, but I could be wrong. If there are contexts where parseable output is really important, we indeed might need to add a new thing for guaranteed parseable output.

So maybe Day[1, 2] is OK?

two-argument vs. three-argument show

I think there are three concepts and only two methods. Ideally, it's nice to have:

  • (a) Human-readable multi-line text representation.
  • (b) Human-readable short text representation (which is used by containers).
  • (c) Parse-able or at least Julia code-like (?) text representation.

3-arg show does (a) and 2-arg show has to do (c) as that's how repr works. The question is what function should do (b). If we let 2-arg show do (b) and (c), it's very tricky to have nice 3-arg show for Array{MyType} (see e.g., https://discourse.julialang.org/t/18951). That's why I suggested in #30757 (comment) to add MIME"application/julia" to the convention so that we can decouple (b) and (c). Alternatively, as I mentioned in the previous comment, letting 3-arg show handle (b) with :compact => true would also decouple them.

@nalimilan
Copy link
Member

TBH I'd rather not discuss the redesign of the printing system here as it's really beyond the scope of this issue. Feel free to file another issue if you have a detailed proposal.

So maybe Day[1, 2] is OK?

Why not. Though as a data point, Unitful.jl represents arrays of values with units like this (which is not parseable):

julia> show(fill(1Unitful.m, 2))
Quantity{Int64,𝐋,Unitful.FreeUnits{(m,),𝐋,nothing}}[1 m, 1 m]

So the equivalent would be "[1 day, 1 day]". It would make sense to use the same convention in Base and in Unitful (in either direction). Cc: @ajkeller34

@omus
Copy link
Member

omus commented Jan 31, 2019

So maybe Day[1, 2] is OK?

I personally don't like this as it appears to be valid Julia code but it isn't. Since we already decided that convert(::Type{Day}, ::Integer) is a non-sensical conversion we may want to instead use Day.([1, 2]) as it is almost as terse but is parsable. I'm also fine with [1 day, 2 days].

Branching on :compact seems to be added to show human-readable format in DataFrame #30200 (comment). But my guess is that the fix should go into DataFrames.jl

I like this idea. Can we implement the fix for DataFrames first?

@JeffBezanson
Copy link
Member

JeffBezanson commented Jan 31, 2019

I personally don't like this as it appears to be valid Julia code but it isn't.

Agree.

Actually I will partly retract my previous comment --- with the printing system as it is now, using :compact to select the human-readable output for show seems like a reasonable solution. I think most of the blame here falls on the fact that 3-arg show for 1-d arrays doesn't touch :compact, but all other ways of printing arrays set :compact=>true. I have actually never liked that. repr and show should not set :compact --- I think that's a bug --- and I'd prefer for 3-arg show to always set it.

@fchorney
Copy link
Contributor

@nalimilan
My apologies. It was my understanding that the output was fine as far as print, string, repl, and show were concerned, except that we still wanted “1 day” to appear in the REPL when declared.

@nalimilan
Copy link
Member

we may want to instead use Day.([1, 2]) as it is almost as terse but is parsable.

We can't print Day.([1, 2]) since we only control the printing of individual entries here.

Branching on :compact seems to be added to show human-readable format in DataFrame #30200 (comment). But my guess is that the fix should go into DataFrames.jl

I like this idea. Can we implement the fix for DataFrames first?

What do you want to fix in DataFrames? AFAICT it uses show just like printing vectors does.

@omus
Copy link
Member

omus commented Jan 31, 2019

What do you want to fix in DataFrames?

At the moment nothing needs to change. If we decide to move away from using the compact show to display the human-readable output then it would be good to adjust DataFrames behaviour so it can continue to show the human-readable output.

@nalimilan
Copy link
Member

I don't think DataFrames should diverge from vector printing.

@tkf
Copy link
Member Author

tkf commented Feb 1, 2019

I personally don't like this as it appears to be valid Julia code but it isn't.

Agree.

@JeffBezanson So show([Day(1)]) should print Day[Day(1)]?

we may want to instead use Day.([1, 2]) as it is almost as terse but is parsable.

We can't print Day.([1, 2]) since we only control the printing of individual entries here.

@nalimilan In principle I think we can add an API (say) convertiblerawvalue(::Type) :: Bool so that containers can use broadcast syntax to implement show. Maybe that's too complicated, though.

@JeffBezanson
Copy link
Member

So show([Day(1)]) should print Day[Day(1)]?

Yes, that's the best we can do. I guess [Day(1)] would be a bit better.

@tkf
Copy link
Member Author

tkf commented Feb 1, 2019

Is [Day(1)] possible with current API? Isn't it as difficult as Day.([1])?

@tkf
Copy link
Member Author

tkf commented Feb 1, 2019

@JeffBezanson Regarding:

I have actually never liked that. repr and 2-arg show should not set :compact --- I think that's a bug --- and I'd prefer for 3-arg show to always set it.

("2-arg" added by me)

A downside of this approach is that :compact is used sometimes really for "compact" output, not just for toggling human-readability. If we apply this to all containers, show(Dict(0=>1)) would print Dict(0 => 1) (with spaces) instead of Dict(0=>1) (current output). I guess that's not desirable?

julia/base/show.jl

Lines 592 to 601 in 0ec1727

function show(io::IO, p::Pair)
iocompact = IOContext(io, :compact => get(io, :compact, true))
isdelimited(io, p) && return show_default(iocompact, p)
typeinfos = gettypeinfos(io, p)
for i = (1, 2)
io_i = IOContext(iocompact, :typeinfo => typeinfos[i])
isdelimited(io_i, p[i]) || print(io, "(")
show(io_i, p[i])
isdelimited(io_i, p[i]) || print(io, ")")
i == 1 && print(io, get(io, :compact, false) ? "=>" : " => ")

@JeffBezanson
Copy link
Member

🤷‍♂️ Dict(0 => 1) seems fine to me.

@StefanKarpinski
Copy link
Member

Seems much more readable to me.

@nalimilan
Copy link
Member

nalimilan commented Apr 20, 2019

@JeffBezanson So do you think we need to change something here? Better not release 1.2 with a new behavior if we want to change it again in 1.3.

In particular, it seems weird to me that the 3-argument show gives the same result as the 2-argument show with :compact=>true. Indeed the manual says that the 3-argument method is for "verbose multi-line printing", so it should rather be similar to :compact=>false.

julia> show(stdout, "text/plain", Day(1))
1 day
julia> show(IOContext(stdout, :compact=>false), Day(1))
Day(1)
julia> show(IOContext(stdout, :compact=>true), Day(1))
1 day

julia> show(stdout, "text/plain", Date(2018, 12, 7))
2018-12-07
julia> show(IOContext(stdout, :compact=>false), Date(2018, 12, 7))
Date(2018, 12, 7)
julia> show(IOContext(stdout, :compact=>true), Date(2018, 12, 7))
2018-12-07

Maybe show should always use the "compact" form if we find it preferable for the 3-argument method?
EDIT: I now see that the problem is that repr is supposed to call show and we shouldn't define repr methods. Ah, well... maybe we don't really need repr to use the Julia syntax?

@JeffBezanson
Copy link
Member

One thing happening here is that printing like 1 day is fine until we are nested inside something that prints using julia syntax like [ ] or =>. Arguably we should never print anything like [1 day, 2 days] or 1 day=>2 days. If people agree with that principle, the solution that comes to mind is to add the :parseable flag, and set it when repr calls show and whenever we nest inside pairs/arrays/etc. Adding a new IOContext property should be non-breaking (except, of course, where we'll use it to fix this issue and change the output).

@JeffBezanson JeffBezanson added the triage This should be discussed on a triage call label Apr 22, 2019
@vtjnash
Copy link
Member

vtjnash commented Apr 22, 2019

Having something like :precedence seems like it could be nice, such that items that know they normally skip printing of ambiguity-resolving delimiters (like Date and Pair) could alter the printing of themselves to add parens or other such modifications. This seems like it might also be better than the current strategy of Pair to conditionally add parens to its arguments based on its complicated, static internal model of how it predicts the component arguments will print.

@tkf tkf changed the title Printing of time-related types in containers is/became inconsistent Printing of time-related types in containers is inconsistent Apr 22, 2019
@tkf
Copy link
Member Author

tkf commented Apr 22, 2019

I think using a new MIME like show(::IO, ::MIME"application/julia", ::T) for repr is better than IOContext because you can make sure to call printer that is intended to be parseable. IOContext-based API basically means "may or may not be parseable" for a caller.

See also #30757

@JeffBezanson
Copy link
Member

A new MIME type might be ok but I think it solves a different problem. The problem here is that this output is weird:

julia> fill(Day(1) => Day(2), 2, 2)
2×2 Array{Pair{Day,Day},2}:
 1 day=>2 days  1 day=>2 days
 1 day=>2 days  1 day=>2 days

The context (REPL display) does not require parseable output, it's just that 1 day=>2 days looks terrible.

@tkf
Copy link
Member Author

tkf commented Apr 22, 2019

OK I think I get caught up the name :parseable. I guess it just means "called inside a container-like object"? Then maybe checking the existence of :typeinfo does the job? That is to say, Day(1) is printed as

  • 1 if :typeinfo is set to Day
  • Day(1) if :typeinfo is set to other types
  • 1 day otherwise

@JeffBezanson
Copy link
Member

That's an interesting idea. I believe the main downside is that it won't give us this output back:

julia> Day.(1:3)
3-element Array{Day,1}:
 1 day
 2 days
 3 days

if that's what we want.

@fchorney
Copy link
Contributor

fchorney commented Jan 9, 2020

Would something like this at least feel consistent?
Doing something like this wouldn't require us to make a new date_str.

That being said, I am still unsure what we would want the solution for the 1 day=>1 day formatting issue to be.

Date
----
julia> d = Date("2019-01-02")
2019-01-02
julia> show([d])
Date[Date("2019-01-02")]
julia> show([d d])
Date[Date("2019-01-02") Date("2019-01-02")]
julia> show(IOContext(stdout, :compact=>true), [d])
Date[2019-01-02]
julia> show(IOContext(stdout, :compact=>false), [d])
Date[Date("2019-01-02")]
julia> fill(d, 2)
2-element Array{Date,1}:
 Date("2019-01-02")
 Date("2019-01-02")
julia> fill(d, 2, 2)
2×2 Array{Date,2}:
 2019-01-02  2019-01-02
 2019-01-02  2019-01-02
julia> fill(d => d, 2)
2-element Array{Pair{Date,Date},1}:
 Date("2019-01-02") => Date("2019-01-02")
 Date("2019-01-02") => Date("2019-01-02")
julia> fill(d => d, 2, 3)
2×3 Array{Pair{Date,Date},2}:
 2019-01-02=>2019-01-02  2019-01-02=>2019-01-02  2019-01-02=>2019-01-02
 2019-01-02=>2019-01-02  2019-01-02=>2019-01-02  2019-01-02=>2019-01-02
julia> print(d)
2019-01-02
julia> repr(d)
"Date(\"2019-01-02\")"

DateTime
--------
julia> d = DateTime("2019-01-01T01:01:01.1")
2019-01-01T01:01:01.1
julia> show([d])
DateTime[DateTime("2019-01-01T01:01:01.1")]
julia> show([d d])
DateTime[DateTime("2019-01-01T01:01:01.1") DateTime("2019-01-01T01:01:01.1")]
julia> show(IOContext(stdout, :compact=>true), [d])
DateTime[2019-01-01T01:01:01.1]
julia> show(IOContext(stdout, :compact=>false), [d])
DateTime[DateTime("2019-01-01T01:01:01.1")]
julia> fill(d, 2)
2-element Array{DateTime,1}:
 DateTime("2019-01-01T01:01:01.1")
 DateTime("2019-01-01T01:01:01.1")
julia> fill(d, 2, 2)
2×2 Array{DateTime,2}:
 2019-01-01T01:01:01.1  2019-01-01T01:01:01.1
 2019-01-01T01:01:01.1  2019-01-01T01:01:01.1
julia> fill(d => d, 2)
2-element Array{Pair{DateTime,DateTime},1}:
 DateTime("2019-01-01T01:01:01.1") => DateTime("2019-01-01T01:01:01.1")
 DateTime("2019-01-01T01:01:01.1") => DateTime("2019-01-01T01:01:01.1")
julia> fill(d => d, 2, 3)
2×3 Array{Pair{DateTime,DateTime},2}:
 2019-01-01T01:01:01.1=>2019-01-01T01:01:01.1  2019-01-01T01:01:01.1=>2019-01-01T01:01:01.1  2019-01-01T01:01:01.1=>2019-01-01T01:01:01.1
 2019-01-01T01:01:01.1=>2019-01-01T01:01:01.1  2019-01-01T01:01:01.1=>2019-01-01T01:01:01.1  2019-01-01T01:01:01.1=>2019-01-01T01:01:01.1
julia> print(d)
2019-01-01T01:01:01.1
julia> repr(d)
"DateTime(\"2019-01-01T01:01:01.1\")"

Periods
-------
julia> d = Day(1)
1 day
julia> show([d])
Day[Day(1)]
julia> show([d d])
Day[Day(1) Day(1)]
julia> show(IOContext(stdout, :compact=>true), [d])
Day[1 day]
julia> show(IOContext(stdout, :compact=>false), [d])
Day[Day(1)]
julia> fill(d, 2)
2-element Array{Day,1}:
 Day(1)
 Day(1)
julia> fill(d, 2, 2)
2×2 Array{Day,2}:
 1 day  1 day
 1 day  1 day
julia> print(d)
1 day
julia> repr(d)
"Day(1)"
julia> fill(d => d, 2)
2-element Array{Pair{Day,Day},1}:
 Day(1) => Day(1)
 Day(1) => Day(1)
julia> fill(d => d, 2, 3)
2×3 Array{Pair{Day,Day},2}:
 1 day=>1 day  1 day=>1 day  1 day=>1 day
 1 day=>1 day  1 day=>1 day  1 day=>1 day

@JeffBezanson
Copy link
Member

Isn't that exactly what was implemented before --- using the :compact flag to select 1 day instead of Day(1)?

@fchorney
Copy link
Contributor

fchorney commented Jan 9, 2020

For that specific type yes. I thought the main issue was that the printing of things wasn't necessarily consistent, so maybe I misunderstood. My example above also changes how Date and DateTime is printed in various situations, which seemed to be consistent among the 3 types (as far as printing a "compact" vs "not compact" output goes).

From what I understand, perhaps there are two issues.

  1. The fact that the different date/time types weren't consistent on when they printed their compact vs non compact forms.
  2. Changing the formatting of the "compact" form for the period types as it looks quite ugly in certain situations. Or rather, making a new formatting flag to designate when we'd want the 1 day kind of output.

Please correct me if I am misunderstanding some aspect of this. I'm trying to wrap my head around what we want the end goal to be, so that I can jump in and start working on the solution.

@nalimilan
Copy link
Member

Just a small point:

julia> show(IOContext(stdout, :compact=>true), [d])
Date[2019-01-02]
julia> show(IOContext(stdout, :compact=>false), [d])
Date[Date("2019-01-02")]

I don't think this difference is justified: we have :typeinfo to decide whether the type information should be repeated, :compact=>false isn't supposed to enable the printing of redundant information.

@JeffBezanson
Copy link
Member

IMO, the goal should be for repr and show output to always be parseable. Plus ideally, whenever these occur in a "syntaxy" context, e.g. inside =>, they should also be parseable. So 1 day can appear as an array element by itself, but 1 day => 1 day should never be printed.

This issue also complains about the difference in printing between vectors and matrices. Ideally they should be the same; that can be done by either (1) turning :compact on for both, or (2) use something other than :compact to pick the 1 day representation.

The only contexts I can think of where 1 day (etc.) makes sense are directly at the prompt (e.g. show with a MIME type), or as a direct array/dataframe element. :compact is not ideal for that because it propagates. So maybe we need a new showelement function that defaults to just calling show with compact set. Date and time types can overload that. Adding a new function sounds extreme, but it looks like the easiest way to get these properties without needing to change lots of code (e.g. changing many types to set a :syntax flag).

@tkf
Copy link
Member Author

tkf commented Jan 10, 2020

showelement sounds good.

Should showelement call 2-arg show? Or 3-arg show? I think it should be 3-arg show as 2-arg show can be large if we impose that it is parseable. But it would then mean that we need to define showelement for containers to cleanly show something like vector of vectors.

@fchorney
Copy link
Contributor

fchorney commented Jan 10, 2020

ok I think I'm starting to understand. Thanks!
So let's just look at Period types for now. Would this be the output that makes sense?

julia> d = Day(1)
1 day

julia> show(d)
Day(1)

julia> showelement(d)
1 day

julia> show([d])
Day[1]

julia> show([d d])
Day[1 1]

julia> show(IOContext(stdout, :compact=>true), [d])
Day[1]

julia> show(IOContext(stdout, :compact=>false), [d])
Day[1]

julia> [d, d][1]
1 day

julia> fill(d, 2)
2-element Array{Day,1}:
 Day(1)
 Day(1)

julia> fill(d, 2, 2)
2×2 Array{Day,2}:
 Day(1)  Day(1)
 Day(1)  Day(1)

julia> print(d)
1 day

julia> string(d)
"1 day"

julia> repr(d)
"Day(1)"

julia> fill(d => d, 2)
2-element Array{Pair{Day,Day},1}:
 Day(1) => Day(1)
 Day(1) => Day(1)

julia> fill(d => d, 2, 3)
2×3 Array{Pair{Day,Day},2}:
 Day(1)=>Day(1)  Day(1)=>Day(1)  Day(1)=>Day(1)
 Day(1)=>Day(1)  Day(1)=>Day(1)  Day(1)=>Day(1)

@fchorney
Copy link
Contributor

I guess Im having an issue with the whole

julia> show([d])
Day[1]

since it seems like above people don't really like this as it looks like parsable julia code but isn't (yet). In the same vain, it seems like people also don't like:

julia> show([d])
Day[Day(1)]

since it duplicates the type.

@JeffBezanson
Copy link
Member

julia> fill(d, 2)
2-element Array{Day,1}:
 Day(1)
 Day(1)

julia> fill(d, 2, 2)
2×2 Array{Day,2}:
 Day(1)  Day(1)
 Day(1)  Day(1)

Those would call showelement, and so could print 1 day. I agree with all the other cases, except Day[Day(1)] doesn't bother me. Repeating type names is better than the situation today IMO.

@JeffBezanson
Copy link
Member

We could allow overloading another function (Base calls it typeinfo_implicit) to indicate that a type's representation fully indicates the type. Then we could at least show a Day array as [Day(1), Day(2)].

@tkf
Copy link
Member Author

tkf commented Jan 10, 2020

What about display([fill(d, 10^3)])?

@JeffBezanson
Copy link
Member

I think that would look like

1-element Array{Array{Day,1},1}:
 Day[Day(1), Day(1) ... Day(1), Day(1)]

@tkf
Copy link
Member Author

tkf commented Jan 11, 2020

Would that be handled by 2-arg show via showelement? Shouldn't showelement call 3-arg show as you can't guarantee evaluable output with :compat/:limit? In that case, I think

1-element Array{Array{Day,1},1}:
 [1, 1, , 1, 1]

or even

1-element Array{Array{Day,1},1}:
 [1 day, 1 day, , 1 day, 1 day]

is possible.

So maybe we need a new showelement function that defaults to just calling show with compact set.

I suppose :limit as well, so that large objects can use .

@JeffBezanson
Copy link
Member

Currently array elements use 2-argument show, so I think showelement should call that. 3-argument show is more intended for displaying an object by itself, not inside another.

I think :compact output can still be parseable, you might just lose some information. :limit obviously won't be parseable, but all it should do is replace elements with .... Something like [1 day, 1 day, …, 1 day, 1 day] might be possible, but not appealing to me and not easy to achieve either. As soon as the [ is printed we're in syntax mode, and we will naturally ask for parseable representations of all the elements.

I suppose :limit as well, so that large objects can use .

No, :limit should basically only be set at the top level. If objects set it in their show methods, then you might get stuck with no way to see the entire contents of something.

@tkf
Copy link
Member Author

tkf commented Jan 11, 2020

Currently array elements use 2-argument show, so I think showelement should call that.

Wasn't this the reason why we don't have parsable repr? People were forced to misuse 2-arg show for printing for-human text outputs to customize display(::Vector{<:Mytype}). I think using 3-arg show for default is an effective (though painful) strategy for propagating this change to downstreams.

If you say that only the call signature is the public API of show and not the detail of output (which practically has been the case in 1.x), this is non breaking.

If objects set it in their show methods, then you might get stuck with no way to see the entire contents of something.

Thanks for pointing it out. I totally missed this point.

@JeffBezanson
Copy link
Member

JeffBezanson commented Jan 12, 2020

I think using 3-arg show for default is an effective (though painful) strategy for propagating this change to downstreams.

Interesting, I think I see what you mean --- for values like days, 3-argument show would print the desired 1 day inside arrays. But in general, 3-argument show can be very free-form, and often contains multiple lines, so it is not really the right thing to use. Then again maybe 3-argument show with :compact set is OK --- that says "we're inside display, so output can be free-form, but please don't use multiple lines since you're inside an array". Is that what you're thinking?

In fact, 3-arg show is never(?) called recursively, so we could potentially use :compact 3-arg show instead of adding showelement.

@tkf
Copy link
Member Author

tkf commented Jan 13, 2020

That's exactly what I'm thinking!

@fchorney
Copy link
Contributor

We could allow overloading another function (Base calls it typeinfo_implicit) to indicate that a type's representation fully indicates the type. Then we could at least show a Day array as [Day(1), Day(2)].

So I'm continuing to work on this. I think this would be a nice improvement. I am unsure how to overload the function typeinfo_implicit.

If I add something such as

function Base.typeinfo_implicit(x::Day)
    return true
end

Doing that seems to not do anything as calling Base.typeinfo_implicit(Day) just called the original function which is defined in arrayshow.jl

@JeffBezanson
Copy link
Member

::Type{Day} should work.

@JeffBezanson
Copy link
Member

OK, I'm on board with trying switching array elements to :compact 3-arg show. Triage can discuss once we have experimental results.

The other discussion item is what to show in Vectors. I'm inclined to just remove the inconsistency and use the same printing as matrices. Printing vector elements non-compactly is nice for showing all digits of numbers, but I'm not sure it's really so important or useful outside of that.

@fchorney
Copy link
Contributor

ok cool, this is my current working output. I'm thinking this satisfies all if not most of the discussion here. Hopefully if this makes people happy, I can move on to getting the other time/date types feeling consistent with this, and I'll open up a PR.

julia> using Dates
julia> d = Day(1)
1 day
julia> show(d)
Day(1)
julia> show([d])
[Day(1)]
julia> show([d d])
[Day(1) Day(1)]
julia> show(IOContext(stdout, :compact=>true), [d])
[Day(1)]
julia> show(IOContext(stdout, :compact=>false), [d])
[Day(1)]
julia> [d, d][1]
1 day

julia> fill(d, 2)
2-element Array{Day,1}:
 1 day
 1 day

julia> fill(d, 2, 2)
2×2 Array{Day,2}:
 1 day  1 day
 1 day  1 day

julia> print(d)
1 day
julia> string(d)
"1 day"

julia> repr(d)
"Day(1)"

julia> fill(d => d, 2)
2-element Array{Pair{Day,Day},1}:
 Day(1) => Day(1)
 Day(1) => Day(1)

julia> fill(d => d, 2, 3)
2×3 Array{Pair{Day,Day},2}:
 Day(1)=>Day(1)  Day(1)=>Day(1)  Day(1)=>Day(1)
 Day(1)=>Day(1)  Day(1)=>Day(1)  Day(1)=>Day(1)

@JeffBezanson
Copy link
Member

Looks perfect to me!

KristofferC pushed a commit that referenced this issue Apr 11, 2020
- Use 3 arg compact show for array elements.
- Add show/print methods for periods
- Periods imply their typeinfo, so set typeinfo_implicit to true for all period types

fixes #30901
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dates Dates, times, and the Dates stdlib module display and printing Aesthetics and correctness of printed representations of objects.
Projects
None yet
Development

No branches or pull requests

8 participants