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

Standardise Julia's Output Ecosystem #13751

Closed
ettersi opened this issue Oct 24, 2015 · 6 comments
Closed

Standardise Julia's Output Ecosystem #13751

ettersi opened this issue Oct 24, 2015 · 6 comments

Comments

@ettersi
Copy link
Contributor

ettersi commented Oct 24, 2015

To a newbie like me, Julia's "object-to-string" ecosystem (functions like print, show, string, ...) appears to be quite a mess. For example, typing [1,2,3] in the REPL calls I-don't-know-which function and prints

3-element Array{Int64,1}:
 1
 2
 3

while print, show and string all produce [1,2,3]. For a symbol :a, on the other hand, REPL and show result in :a while print and string produce only a without the colon. Clearly, these different output formats appeared because object-to-string conversion has different purposes in different contexts, but it is not clear to me what these contexts are and how they are mapped to the above functions. In my opinion, it would be worth to work out these contexts and document when you should use which function because of the following reasons.

  • You essentially force users to address this issue because the REPL always prints return values and the default formatting for custom types is rather ugly and confusing.
  • At this point, it is virtually impossible to write generic code. If I write a to-string function for my custom type, should it produce a one-liner in order to work nicely with the REPL formatting for arrays, or should I focus more on producing an easily readable and thus possibly multi-line output? Conversely, if I implement a composite type, should I call print or show to display nested objects?
  • Usability and aesthetics. I assume we all know the situation where you waste hours on debugging simply because you didn't look at the decisive information as doing so would have been somewhat tedious. Providing a well-designed user interface helps a lot in preventing these situations, thus the core library should simplify and encourage its implementation.

Here are my thoughts on what key characteristics of to-string functions are:

  • single line vs. multi-line.
  • Information-preserving vs. summarising. If you print a float in the REPL, the first 4 significant digits are more than enough in most cases, while if you store output in a text file you might want to have all digits.
  • Technical vs. semantic. E.g. show(:a) -> :a (technical) vs. print(:a) -> a (semantic).

In my opinion:

  • Every type should provide a single-line, technical to-string function which can be called by composite types for recursive printing. For most types, this will have to be summarizing.
  • Every type should provide a pretty-printing function with characteristics depending on what makes sense for the given type. This is the function which should be called by the REPL but not used in recursions.
  • string should only be provided for types which can be information-preserving on a single line (e.g. numbers, symbols, expressions) and follow the semantic principle.

Related:

@StefanKarpinski
Copy link
Member

This definitely needs some rationalization, although I don't think that print and show are the worst offenders – it's things like showcompact, showerror and the haze of display-related functions in show.jl that really bother me. If we could systematize all of that in a way that lets us generically do automatic limited output like we do for matrices, I would be thrilled. I like your taxonomy of characteristics a lot. I don't agree that printing a few digits of a float is sufficient because lying about floating point causes a lot of confusion and anguish. We do truncate floats in arrays, however, which I suspect we should indicate by printing them as 1.234… or something. I'm not sure about the terms "technical" vs "semantic" – is there somewhere you're getting those terms from? I agree that the distinction is a good one, however.

@jiahao
Copy link
Member

jiahao commented Oct 24, 2015

See also #7959

@ettersi
Copy link
Contributor Author

ettersi commented Oct 24, 2015

The problem with print and show is that they are very poorly documented. Why are there two functions for essentially the same thing? I conjecture that this is part of the reason for all the spin-offs you mention. If your given a gun without a manual, it's safer to build your own rather than running the risk of shooting your foot.
"Technical" and "semantic" are my own invention. Another, possibly less disputable example for the difference between information-preserving and summarising are containers: should they just say "I'm an Array{T,N} with n elements" or should they list all their elements.

@JeffBezanson
Copy link
Member

I think this is covered by #5709, #7959, #29, #6117, and #9458. It would be especially helpful to comment on the proposal in #7959.

I don't think show and print are especially poorly documented:

help?> show
search: show showall showerror showcompact @show Cshort Cushort print_shortest

  show(x)

  Write an informative text representation of a value to the current output
  stream. New types should overload show(io, x) where the first argument is a
  stream. The representation used by show generally includes Julia-specific
  formatting and type information.

help?> print
search: print println print_joined print_escaped print_shortest print_unescaped

  print(x)

  Write (to the default output stream) a canonical (un-decorated) text
  representation of a value if there is one, otherwise call show. The
  representation used by print includes minimal formatting and tries to avoid
  Julia-specific details.

Feel free to make a pull request with suggested wording improvements.

@ettersi
Copy link
Contributor Author

ettersi commented Oct 24, 2015

Fair enough, I correct my statement. The documentation of show and print is actually fine as it precisely specifies what the functions do, and in particular it covers the difference between print(:a) and show(:a) I mentioned as "confusing". It gives no hint about why/when I would prefer one over the other, though. At the moment, I mostly use print simply because this allows me to do print(4,2), but that's probably the wrong reason and basing decisions on arguments I assume to be wrong leaves a bad feeling in my stomach.
I feel like I don't have the necessary background to comment on #7959. If you truly want people not familiar with Julia's output system to participate in the discussion, someone should write a gentle introduction to the topic (not a request, just a remark).

@IainNZ
Copy link
Member

IainNZ commented Oct 25, 2015

@ettersi if you find the time, maybe after reading through those past issues you could write such a summary? We're all volunteers of course, most of us with jobs that aren't anything to do with Julia development :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants