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

display non-compactly arrays with only one column #22981

Merged
merged 3 commits into from
Aug 3, 2017

Conversation

rfourquet
Copy link
Member

When there is only one column, there is almost all the horizontal space to print one element, so this can as well be printed non-compactly.
Together with #22916 and #22899, this allows to print vectors of pairs more similarly to Dict:

julia> d = Dict(1=>22, 33=>4)
Dict{Int64,Int64} with 2 entries:
  33 => 4
  1  => 22

julia> sort!(collect(d)) # I always do this
2-element Array{Pair{Int64,Int64},1}:
  1 => 22
 33 => 4

julia> sort!(collect(d)) # before those PRs
2-element Array{Pair{Int64,Int64},1}:
 1=>22
 33=>4

The last difference I would like to see addressed eventually is the alignment of the first elements of the pairs.

@rfourquet rfourquet added the display and printing Aesthetics and correctness of printed representations of objects. label Jul 27, 2017
@StefanKarpinski
Copy link
Member

How about making it non-compact whenever the non-compact form would fit in the screen?

@rfourquet
Copy link
Member Author

How about making it non-compact whenever the non-compact form would fit in the screen?

Yes, I planned to work on this, together with fixing some patholigical (in my opinion) cases like [1, 2, big(2)^3000, 4]: I would prefer only one item per line, like in the Dict case, but at least that other elements which need only one line (like 1) occupy only one line. But I (or someone else?) will need more time for that, and I thought I could fix the simple case first (also to see if there is support).

@@ -1386,7 +1386,10 @@ end
function alignment(io::IO, x::Pair)
s = sprint(0, show, x, env=io)
if has_tight_type(x) # i.e. use "=>" for display
left = length(sprint(0, show, x.first, env=io)) + 2isa(x.first, Pair) # 2 for parens
iocompact = IOContext(io, :compact => get(io, :compact, true))
left = length(sprint(0, show, x.first, env=iocompact))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated, but this env kwarg is completely undocumented...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I just copy-pasted from above...

@StefanKarpinski
Copy link
Member

Fair enough, this is an improvement.

@rfourquet
Copy link
Member Author

CI failures are 2 timeouts. Anyone opposed to this change?

@rfourquet rfourquet merged commit fe09a7b into master Aug 3, 2017
@rfourquet rfourquet deleted the rf/array-show-noncompact branch August 3, 2017 08:08
@nalimilan
Copy link
Member

Sorry for reacting so late, but I'm not a fan of that behavior. "Compact" printing is not (only?) about the amount of spacing used in the string representation, it's mainly about whether type information is printed or not. AFAIK the idea is that when printing elements inside an array, there's no point in repeating the type information for each element. Indeed, ?showcompact says:

  This is used for printing array elements without repeating type information
  (which would be redundant with that printed once for the whole array), and
  without line breaks inside the representation of an element.

Concretely, the new behavior is annoying for CategoricalArrays. On Julia 0.6, we have this:

julia> CategoricalArray(["a", "b"])
2-element CategoricalArrays.CategoricalArray{String,1,UInt32,String,Union{}}:
 "a"
 "b"

But on 0.7:

julia> CategoricalArray(["a", "b"])
2-element CategoricalArrays.CategoricalArray{String,1,UInt32,String,Union{}}:
 CategoricalArrays.CategoricalValue{String,UInt32} "a"
 CategoricalArrays.CategoricalValue{String,UInt32} "b"

I don't see a way to keep a readable array printing on Julia 0.7 without also removing all type information when printing CategoricalValue elements individually (i.e. with compact=false). That's problematic, since printing these as just "a" or "b" would mislead users into thinking they are plain strings, which they aren't.

Maybe we need to distinguish several printing attributes, one for the amount of spacing, and one for the type information? But is the presence of one or two spaces really a big deal? That looks negligible compared with the verbosity of printing all type information.

@rfourquet
Copy link
Member Author

Since this PR, I also got annoyed by redundant type information which is not satisfying; so I'm still hoping for something better. Thanks for pointing out the definition of showcompact: it suggests that two concepts are conflated: removing redundant type information for arrays, and squeezing spaces to fit on screen. My prefered options are either introducing a new context property, or having printing pairs/complexes etc. independant of the compact property.

@StefanKarpinski
Copy link
Member

StefanKarpinski commented Aug 28, 2017

I was discussing this with @JeffBezanson the other day and had the realization that we've been using the :compact I/O context to mean two different things that happen to often occur together when printing arrays:

  1. Print a more compact version of a value, discarding the less significant parts to save space. The classic example here is printing only a few significant digits of a floating-point values.

  2. Don't print information that will already be clear from a typed container. E.g. in the classic ModInt example, when printing an array of ModInt{13} printing each element as k mod 13 is just redundant – we can just print an element as k since we've already indicated the type of the whole array.

The former is something you only want to do when you're actually low on space, so not, for example, when printing a single-column array where there's plenty of horizontal space. The latter is something you want to do in concretely-typed array context no matter what. We may want to separate these two senses of compactness into a :compact context with the former meaning and a :typed context indicating what the context already indicates about the type of a value so that we can skip printing more information about the type of a specific value than necessary. I'm imagining this could look something like IOContext(io, :typed => ModInt{13}) which the ModInt show method can look at to tell that the array already indicates the exact type of each value so the mod 13 part is unnecessary. If the context where IOContext(io, :typed => ModInt) instead, you'd know you were printing in an abstractly typed context and the mod 13 part is still necessary, because even though you know you're printing modular integers, the modulus isn't known.

@nalimilan
Copy link
Member

Makes sense. Note that we have the limit property too (documented in ?IOContext), which is currently used only to omit some elements when printing a container. I don't know why we introduced it in addition to compact, i.e. why it was useful to have different properties to affect the printing of containers and that of its elements. Maybe it wouldn't be as useful if we stopped using compact to omit type information.

@StefanKarpinski
Copy link
Member

This stuff could definitely use a final design pass. The array printing code is very old and crufty.

@KristofferC
Copy link
Member

KristofferC commented Oct 4, 2017

This made vectors of numbers print in full precision:

julia> rand(5)
5-element Array{Float64,1}:
 0.9318527928490181
 0.2744201444222154
 0.489516198172518 
 0.7882092581043787
 0.6460356677605974

intended? @rfourquet

@rfourquet
Copy link
Member Author

Yes this was intended, the idea here being that there is usually enough room to show the elements non-compactly. But as noted in the last few comments, the situation is still unsatisfying and requires some more decisions. Does this particular example bother you? (besides the doctests as you noted on Slack, which I totally forgot about ;-) )

@KristofferC
Copy link
Member

In my opinion, it is quite rare to be interested in this many significant digits so it gives a more noisy impression for little benefit, c.f. format short by default in Matlab

@rfourquet
Copy link
Member Author

I must say I would tend to agree, and this example was not the motivation for this PR. But in other cases, like printing complex numbers or pairs, it was frustrating to have vector printing try to save space when it was making it less readable unnecessarily. I would volunteer to improve this, but we don't have a clear solution yet... maybe a specific option in IOContext for floating point numbers, to approximate Matlab's format short? (I'm personally bothered by the scientific format of BigFloat). I think the idea of having a way to modify the REPL's IOContext was discussed somewhere.

@KristofferC
Copy link
Member

Could check if the type is an AbstractVector{<:Number} and print it compactly in that case.

@nalimilan
Copy link
Member

Could check if the type is an AbstractVector{<:Number} and print it compactly in that case.

That sounds too ad hoc to me. We need a general solution which allows custom types to choose the most appropriate behavior. For example, CategoricalValue objects should also print compactly (see above).

Adding options to IOContext to choose the number of decimals sounds good.

@StefanKarpinski
Copy link
Member

In my opinion, it is quite rare to be interested in this many significant digits so it gives a more noisy impression for little benefit

How is this argument specific to vectors? This makes it sound like you just want to always print a limited number of digits of numbers, which strikes me as pretty unjulian. Wanting to see all of the digits of numbers in vectors was one of the motivations of this change.

There are two issues that do get conflated into the :compat I/O context that would be good to untangle, however:

  1. Are we in a context where type information is redundant? If so, then don't print verbose type information. The canonical example is ModInt which prints as k mod n as scalars and just as k in array context. But of course, if the context is an Array{>:ModInt} (i.e. no specific modulus) then printing the modulus would be appropriate.

  2. Are we in a context where we need to save space? If so, sacrifice some less significant information to fit more information on screen. The canonical example of this is printing numbers with reduced precision. Other examples could include omitting less interesting fields of objects.

Both of these should operate through I/O contexts. Maybe IOContext(:typeinfo => ModInt{n}) versus IOContext(:typeinfo => ModInt) (default :typeinfo => Any implied when there's no type information) to indicate how much type information is implied, and reuse IOContext(:compact => [true|false]) for the latter case?

@StefanKarpinski
Copy link
Member

StefanKarpinski commented Oct 4, 2017

Oh, I guess :sigdigits would be a more specific I/O context option than just :compact. Actually, we probably need more generic type-specific formatting controls.

@KristofferC
Copy link
Member

To me it is not clear if this is a net gain as it is. In some cases it is better, and in some cases it seems worse. My opinion would be to revert this PR and add it back when we have a method to differ between the conflated IO contexts.

Do you have any thoughts on this @rfourquet?

@StefanKarpinski
Copy link
Member

I would prefer not to revert this. What is the case for vertical vector printing context being "compact"? It seems like the entire issue is just that extra significant digits are a hassle sometimes.

@KristofferC
Copy link
Member

Alright, perhaps implementing something like sigdigits wouldn't be so hard with the new Option struct for the REPL.

@rfourquet
Copy link
Member Author

I also would prefer to not revert, as printing compactly unnecessarily is worse in my view than printing too much. For the number of significant digits, I think it's a matter of personal preference, which maybe could indeed be handled via REPL.Options. The other problem of redundant type information is more serious I think (like in zeros(Float16, 2)), to which Stefan suggested a solution. I will try to come up with a PR in the next few weeks if no-one beats me to it.

@nalimilan
Copy link
Member

@rfourquet Any news?

@rfourquet
Copy link
Member Author

I've been carried away, but your ping motivates me to start working again on this.

rfourquet added a commit that referenced this pull request Nov 19, 2017
Before, the :compact property was conflating 2 concepts:
1) are we low on screen space?
2) can we skip printing individual type information for elements in a collection?

Cf. #22981 for context and discussion. Credit to Stefan Karpinski for the
formulation of the design implemented here.
rfourquet added a commit that referenced this pull request Nov 19, 2017
Before, the :compact property was conflating 2 concepts:
1) are we low on screen space?
2) can we skip printing individual type information for elements in a collection?

Cf. #22981 for context and discussion. Credit to Stefan Karpinski for the
formulation of the design implemented here.
rfourquet added a commit that referenced this pull request Dec 6, 2017
Before, the :compact property was conflating 2 concepts:
1) are we low on screen space?
2) can we skip printing individual type information for elements in a collection?

Cf. #22981 for context and discussion. Credit to Stefan Karpinski for the
formulation of the design implemented here.
rfourquet added a commit that referenced this pull request Dec 6, 2017
Before, the :compact property was conflating 2 concepts:
1) are we low on screen space?
2) can we skip printing individual type information for elements in a collection?

Cf. #22981 for context and discussion. Credit to Stefan Karpinski for the
formulation of the design implemented here.
rfourquet added a commit that referenced this pull request Dec 7, 2017
Before, the :compact property was conflating 2 concepts:
1) are we low on screen space?
2) can we skip printing individual type information for elements in a collection?

Cf. #22981 for context and discussion. Credit to Stefan Karpinski for the
formulation of the design implemented here.
rfourquet added a commit that referenced this pull request Dec 7, 2017
Before, the :compact property was conflating 2 concepts:
1) are we low on screen space?
2) can we skip printing individual type information for elements in a collection?

Now, :compact controls only 1), while :typeinfo controls 2).
Cf. #22981 for context and discussion. Credit to Stefan Karpinski for the
formulation of the design implemented here.
rfourquet added a commit that referenced this pull request Dec 7, 2017
Before, the :compact property was conflating 2 concepts:
1) are we low on screen space?
2) can we skip printing individual type information for elements in a collection?

Now, :compact controls only 1), while :typeinfo controls 2).
Cf. #22981 for context and discussion. Credit to Stefan Karpinski for the
formulation of the design implemented here.
evetion pushed a commit to evetion/julia that referenced this pull request Dec 12, 2017
Before, the :compact property was conflating 2 concepts:
1) are we low on screen space?
2) can we skip printing individual type information for elements in a collection?

Now, :compact controls only 1), while :typeinfo controls 2).
Cf. JuliaLang#22981 for context and discussion. Credit to Stefan Karpinski for the
formulation of the design implemented here.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
display and printing Aesthetics and correctness of printed representations of objects.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants