I think #30817 changed show(io, "text/plain", ::Array{Day})
etc. On cb7a5691a51b6bbc0e670f601d6269d893340e44 (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 (c0e9182fa94e2b96106e8e4c14cb36dc9d35cb40) 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
Matrix printing sets :compact=>true
, vector printing doesn't. And indeed since https://github.com/JuliaLang/julia/pull/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
We should remove the branch that switches to 1 day
based on the :compact
property. We can optionally add typeinfo
support as well.
@tkf Would you make a pull request?
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 https://github.com/JuliaLang/julia/pull/30200#issuecomment-444279588. 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.
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".)
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.
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 (https://github.com/JuliaLang/julia/pull/30757#issuecomment-456973443) 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:
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 https://github.com/JuliaLang/julia/pull/30757#issuecomment-456936172 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.
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
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 https://github.com/JuliaLang/julia/pull/30200#issuecomment-444279588. But my guess is that the fix should go into DataFrames.jl
I like this idea. Can we implement the fix for DataFrames first?
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.
@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.
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.
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.
I don't think DataFrames should diverge from vector printing.
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.
So
show([Day(1)])
should printDay[Day(1)]
?
Yes, that's the best we can do. I guess [Day(1)]
would be a bit better.
Is [Day(1)]
possible with current API? Isn't it as difficult as Day.([1])
?
@JeffBezanson Regarding:
I have actually never liked that.
repr
and 2-argshow
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?
https://github.com/JuliaLang/julia/blob/0ec17276a6055fea31971eaad109d5888b9edd16/base/show.jl#L592-L601
:man_shrugging: Dict(0 => 1)
seems fine to me.
Seems much more readable to me.
@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?
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).
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.
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
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.
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 types1 day
otherwiseThat'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.
I think the output in my proposal
julia> Day.(1:3)
3-element Array{Day,1}:
1
2
3
is compatible with the way Array{Bool}
uses 0/1 #30575.
Except that false == 0
holds but Day(1) == 1
does not, so it's not quite the same situation.
Ah, yes. I also realized that my proposal is (somewhat?) problematic since it'd do
julia> show(Day.(1:2))
Day[1, 2]
But Day[1, 2]
is invalid (even though it is parseable) since convert(::Type{Day}, ::Int)
is not defined (as already discussed).
IIRC, there is no way for Day
to behave differently inside show(::IO, ::Array{Day})
and show(::IO, ::MIME"text/plain", ::Array{Day})
because show(::IO, ::Day)
is used inside both of them https://github.com/JuliaLang/julia/issues/30901#issuecomment-459142574. It would be nice if 2-arg and 3-arg show
worlds are clearly separated so that each type T
has some control of printing of "Container{T}
" in 2-arg and 3-arg show
. In 3-arg show
of containers, I think using show(IOContext(io, :compact=>true, ...), "text/plain", x)
to print element x
in the container makes more sense than using 2-arg show
.
I think the tradeoff there is that the text/plain
output for 1-d arrays sets :compact=>false
, so the elements of vectors-of-vectors would use the long multi-line format, which doesn't really work. Basically you'd have to set :compact=>true
for everything inside containers, which is quite inflexible.
Overall the problem here is that we use features of the printing system for not-quite their intended meanings. E.g. 1 day
is not a "compact" form of Day(1)
, it's just a totally different style of printing. A secondary problem is ad-hoc decisions like printing matrix elements compactly, but not vector elements.
Let me take back the comment about "clearly separating 2-arg and 3-arg show
worlds." I think reducing human readability as you go inside nested object like show(::IO, ::MIME"text/plain", ::Vector{Vector{Day}})
-> show(::IO, ::Vector{Day})
-> "repr(::IO, ::Day)
" (or application/julia
) would make sense. Maybe not being able to do something like this was what you meant by "quite inflexible?"
Overall the problem here is that we use features of the printing system for not-quite their intended meanings.
I'd like to add that the intended meanings have been unclear (at least to me). It looks like 2- and 3-arg show
are used for
But it's not possible to consistently choose one of those at the moment. I think this lack of consistency leads to problems like inability to choose 1 day
or Day(1)
and also:
A secondary problem is ad-hoc decisions like printing matrix elements compactly, but not vector elements.
Idea from triage on the fastest way to resolve this for 1.2: use 1 day
only for the MIME"text/plain"
format, and tell DataFrames to call that.
Also noted that Unitful.jl has the same problem, printing 1 meter as 1 m
. But, it uses that printing everywhere, so at least it's consistent.
We also discussed calling a new flag for this :repr
. In the future, I think it would be good to have so we can print 1 day
inside arrays but Day(1)=>Day(2)
inside pairs etc.
Looking back at @nalimilan 's comment
That's doesn't make a lot of sense to me since both representations take the same number of characters.
makes me think the problem might really be that simple. For example
julia> a = fill(now(),3)
3-element Array{DateTime,1}:
DateTime(2019, 4, 25, 15, 41, 32, 171)
DateTime(2019, 4, 25, 15, 41, 32, 171)
DateTime(2019, 4, 25, 15, 41, 32, 171)
julia> repeat(a,1,2)
3×2 Array{DateTime,2}:
2019-04-25T15:41:32.171 2019-04-25T15:41:32.171
2019-04-25T15:41:32.171 2019-04-25T15:41:32.171
2019-04-25T15:41:32.171 2019-04-25T15:41:32.171
is also inconsistent, but you forgive it since the compact format really is more compact. While 1 day
is not smaller than Day(1)
so it's not clear why we'd switch to that. So maybe this only applies to periods, or maybe we should just do nothing?
you forgive it since the compact format really is more compact
Well, I personally never "forgive" it :wink:. I've been using Day
as it's easier to construct. I think it's better to have a consistent treatment for all time-related types (or rather _any_ types).
For example, we currently have:
julia> show([1=>2]); println()
Pair{Int64,Int64}[1 => 2]
julia> show(IOContext(stdout, :compact=>true), [1=>2]); println()
Pair{Int64,Int64}[1=>2]
However, Vectpr{Date}
behaves differently
julia> show([Date(2000, 01, 01)]); println()
Date[Date(2000, 1, 1)]
julia> show(IOContext(stdout, :compact=>true), [Date(2000, 01, 01)]); println()
Date[2000-01-01]
If we want Date[Date(2000,1,1)]
to be in the last output, we need to avoid switching behavior of show(::IO, ::Date)
based on :compact
. Or maybe Date[2000-01-01]
is what we want?
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.
FWIW: the system used by Pair
is not so static: isdelimited(io::IO, x::X)
, if defined, indicates whether parenthesis are needed around the output of show(io, x)
in ambiguous situations. So isdelimited
has access to the current IOContext
to make its decision. So it's an extensible and hopefully not so complicated system, which doesn't have to be restricted to pairs (although I'm certainly not claiming it's a good enough general system that doesn't need a redesign).
So with the current implementation of show for Day
, the following could be defined:
julia> Base.isdelimited(io::IO, x::Day) = !get(io, :compact, false)
julia> fill(Day(1) => Day(2), 2)
2-element Array{Pair{Day,Day},1}:
Day(1) => Day(2)
Day(1) => Day(2)
julia> fill(Day(1) => Day(2), 2, 3)
2×3 Array{Pair{Day,Day},2}:
(1 day)=>(2 days) (1 day)=>(2 days) (1 day)=>(2 days)
(1 day)=>(2 days) (1 day)=>(2 days) (1 day)=>(2 days)
(PS: I would remove the :compact
check when showing Day
which indeed doesn't seem to have to do with compact printing)
Idea from triage on the fastest way to resolve this for 1.2: use
1 day
only for theMIME"text/plain"
format, and tell DataFrames to call that.
@JeffBezanson I don't understand why DataFrames keeps coming up in this thread. It just uses the same printing as matrices (i.e. 2-argument show
with :compact=>true
. It wouldn't make sense to switch to the three-argument text/plain method AFAICT since it's supposed to be used for multi-line printing.
So maybe this only applies to periods, or maybe we should just do nothing?
What do you mean by "do nothing"? Keep the current state of master, or go back to 1.1?
Overall I agree that :compact
shouldn't be used for radical changes to the printing format like this: it should just change the spacing, the number of digits or the amount of detail printed.
I don't understand why DataFrames keeps coming up in this thread.
It's just an example of a case where the 1 day
output might be desired.
What do you mean by "do nothing"?
I mean keep the current state of master.
(PS: I would remove the
:compact
check when showingDay
which indeed doesn't seem to have to do with compact printing)
Yes, that might be the best option. 1 day
is not any particularly standard format, so we might as well use Day(1)
. It's different for other time types, e.g.
julia> now()
2019-04-28T14:00:39.635
julia> repr(now())
"DateTime(2019, 4, 28, 14, 0, 46, 84)"
where there is a significant advantage to using the former output e.g. in dataframes.
While the issue here only needs to deal with the question for dates, I have another type that ran into the same sorts of problems: Polynomial. Notably (with respect to my musing that "parsable" is actually "precedence"), I found that this package currently uses that idea somewhat explicitly: https://github.com/JuliaMath/Polynomials.jl/blob/96b9a210776d6b212fb21df38d8b07db1fd1e629/src/show.jl#L177
Another datapoint, we see that Complex
treats the ":compact" flag as meaning it should assume that it's printed inside an array (similar to Pair, dropping the space under the assumption that makes it visually unambiguous)
But then we also see that Polynomials had to intercept the printing for Complex because of this: https://github.com/JuliaMath/Polynomials.jl/blob/master/src/show.jl#L185, and has an example using Dual numbers and precedence showing how to extend it further.
It's different for other time types
That does seem bad. I think my preference here would be of a third option, which could be one of:
DateTime(2019-04-28T14:00:39.635)
‘2019-04-28T14:00:39.635’
DateTime(Y=2019, m=4, d=28, H=14, M=0, S=39, s=635)
which is not necessarily parsable or directly useable (or more compact), but any of which could satisfy the precedence expectation of this show context(*). (I think the first is better for this type, but include the others as examples of what I think other types might instead choose to do in the similar situation)
I think the first is better for this type, but include the others as examples of what I think other types might instead choose to do in the similar situation
My understanding is that what you want to do is to make the "tree" structure of the object explicit even if it is not parsable. If so, ‘2019-04-28T14:00:39.635’
or even (2019-04-28T14:00:39.635)
sounds like a good option. That is to say, the :precedence
is used for clarifying the "object boundary", not for clarifying the object type.
Changes reverted on the backports branch. Moving to 1.3.
@nalimilan, wasn't reverting the incomplete changes your idea?
Yes -- I clicked on the wrong button. :-)
From triage: since we have been reverting this for every release, we should probably revert it on master. Then at some point we can try to implement printing as date"0000-00-00"
(keeping 0000-00-00
in compact mode). Not as clear how to handle Day(1)
though.
I was hoping to tackle this myself at some point soon. Though ideally we can come to a consensus about what we want with the period types and I would be happy to do the work.
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.
----
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
Isn't that exactly what was implemented before --- using the :compact
flag to select 1 day
instead of Day(1)
?
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 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.
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.
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).
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.
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)
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.
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.
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)]
.
What about display([fill(d, 10^3)])
?
I think that would look like
1-element Array{Array{Day,1},1}:
Day[Day(1), Day(1) ... Day(1), Day(1)]
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 callingshow
with compact set.
I suppose :limit
as well, so that large objects can use …
.
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.
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.
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
.
That's exactly what I'm thinking!
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
::Type{Day}
should work.
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.
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)
Looks perfect to me!
Most helpful comment
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.