# \`show\` is too low-level

**URL:** <https://discourse.julialang.org/t/show-is-too-low-level/7531>\
**Category:** General Usage\
**Tags:** plea\
**Created:** [December 5, 2017, 1:48pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531 "2017-12-05T13:48:37Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)\
**Post date:** [December 5, 2017, 1:48pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/1 "2017-12-05T13:48:37Z")

</div>

The printing of tables has been discussed recently, and every time I wonder how many different table-printing implementations, or tree-printing implementations are in the Julia ecosystem.

Here is how `show` works:

> [@ANN: RegressionTables.jl produces publication-quality regression tables](https://discourse.julialang.org/t/ann-regressiontables-jl-produces-publication-quality-regression-tables/7516/4):
>
> Regarding the rendering, the Julian approach is to implement various show methods for your RegressionTable type, one for each backend (plain text, LaTeX, Markdown, HTML, CSV…)

As a package developer, each new type I write should ideally implement a `show` method for each of these `MIME` types. Plus `Atom`, which sometimes needs special handling. And maybe Visual Studio…? That’s a lot of work! Even Base doesn’t bother; `Base.Profile.print`, and `Base.whos` are pure ASCII, even in IJulia.

What I would like would be to just say “render this type as a table, with numbers below 0 in red”, then Julia, or some mystery package, would handle all the different MIME types to the best of that MIME type’s capabilities. Something like

```julia
using VisualElements # to be written!

struct FamilyTreeNode
    person_name
    children
end

function VisualElements.renderas(node::FamilyTreeNode)
    # Display FamilyTreeNode objects as trees, and
    # highlight the people with no children.
    node_name = (isempty(node.children) ? 
                 VisualElements.emphasize(node.person_name) : 
                 node.person_name)
    VisualElements.tree(node_name,
                        map(renderas, node.children))
end

```

In my ideal world, `renderas` would be called from the fallback `show` method in Base, but this isn’t strictly necessary. `VisualElements` could offer a macro that implements all the `show` methods for a particular type.

I’m sorry to say that I can’t make this happen, but with 1.0 nearing, I wanted to throw it out there in case it inspires someone to get the ball rolling. `show` is nice, but it’s too low-level for most use cases.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 5, 2017, 1:55pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/2 "2017-12-05T13:55:34Z")

</div>

Would it help if there was an intermediate format, with `show` defined?

Eg

```julia
function show(io::IO, mime, node::FamilyTreeNode)
    r = construct_representation(node)
    show(io, mime, r)
end

```

This could be done as a library later on.

---

<div class="post-metadata">

**Author:** ![nalimilan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nalimilan/32/147_2.png) [@nalimilan](https://discourse.julialang.org/u/nalimilan)\
**Post date:** [December 5, 2017, 1:58pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/3 "2017-12-05T13:58:34Z")

</div>

Isn’t this what `Markdown.Table` does already?

---

<div class="post-metadata">

**Author:** ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)\
**Post date:** [December 5, 2017, 2:23pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/4 "2017-12-05T14:23:11Z")

</div>

Maybe? I couldn’t find any documentation for it. How would the above example be written with `Markdown`? One problem is that `Base.show(io::IO, mime, node::FamilyTreeNode) = ...` creates a method ambiguity.

---

<div class="post-metadata">

**Author:** ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)\
**Post date:** [December 5, 2017, 2:26pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/5 "2017-12-05T14:26:05Z")

</div>

> [@Tamas\_Papp](#):
>
> Would it help if there was an intermediate format, with show defined?

Yes, that’s what I’m suggesting (my `renderas` is your `construct_representation`). We’d have `construct_representation(x) = x` by default in Base, then some visual representation package X.jl would define `show` methods for many different intermediate representations, and package developers/users would import `X.jl` and implement `construct_representation(x::MyType) = <build some X.jl objects>`

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 6, 2017, 12:57pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/6 "2017-12-06T12:57:37Z")

</div>

Yes — I just didn’t know about it 😄

Apparently all the elements are in place for using `Markdown` as an interim construct, then printing nicely:

```julia
julia> t = Markdown.Table([["my", "favorite", "numbers"],
                           string.([19, 42, 137])],
                          [:l, :c, :r])
Base.Markdown.Table(Array{Any,1}[Any["my", "favorite", "numbers"], Any["19", "42", "137"]], Symbol[:l, :c, :r])

julia> m = Markdown.MD(t)
my favorite numbers
–– –––––––– –––––––
19 42 137

julia> show(STDOUT, MIME"text/latex"(), m)
\begin{tabular}
{l | c | r}
my & favorite & numbers \\
\hline
19 & 42 & 137 \\
\end{tabular}

julia> show(STDOUT, MIME"text/html"(), m)
<div class="markdown"><table><tr><th>my</th><th>favorite</th><th>numbers</th></tr><tr><td>19</td><td>42</td><td>137</td></tr></table>
</div>

```

(PS: I didn’t know we had a `plea` tag, but I like it 😉)

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 10, 2017, 5:57pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/7 "2017-12-10T17:57:58Z")

</div>

I tried to use the above to define `show` methods using `Markdown` as the interim representation:

```julia
import Base.Markdown

struct Foo
    x
end

function Base.show(io::IO, mime, f::Foo)
    t = Markdown.Table([[string(f.x)]], [:c])
    m = Markdown.MD(t)
    show(io, mime, m)
end

```

but I get

```julia
julia> Foo(1)
Error showing value of type Foo:
ERROR: MethodError: no method matching display(::Foo)
Closest candidates are:
  display(::Any) at multimedia.jl:191
  display(::Display, ::AbstractString, ::Any) at multimedia.jl:125
  display(::AbstractString, ::Any) at multimedia.jl:126
  ...
Stacktrace:
 [1] display(::Foo) at ./multimedia.jl:201
 [2] eval(::Module, ::Any) at ./boot.jl:235
 [3] print_response(::Base.Terminals.TTYTerminal, ::Any, ::Void, ::Bool, ::Bool, ::Void) at ./REPL.jl:144
 [4] print_response(::Base.REPL.LineEditREPL, ::Any, ::Void, ::Bool, ::Bool) at ./REPL.jl:129
 [5] (::Base.REPL.#do_respond#16{Bool,Base.REPL.##26#36{Base.REPL.LineEditREPL,Base.REPL.REPLHistoryProvider},Base.REPL.LineEditREPL,Base.LineEdit.Prompt})(::Base.LineEdit.MIState, ::Base.AbstractIOBuffer{Array{UInt8,1}}, ::Bool) at ./REPL.jl:646

```

am I doing this the wrong way?

---

<div class="post-metadata">

**Author:** ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)\
**Post date:** [December 10, 2017, 7:46pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/8 "2017-12-10T19:46:49Z")

</div>

That’s interesting; Markdown is irrelevant, you get the same error with an empty function body for `show`. Meanwhile, in IJulia, the same code yields a method ambiguity error.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 13, 2017, 4:23pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/9 "2017-12-13T16:23:18Z")

</div>

This works in the terminal and Jupyter notebooks:

```julia
import Base: display, show
using Base.Markdown: @md_str, html

struct Fancy end

markdown_representation(::Fancy) = md"*emph* ``α``"

display(x::Fancy) = display(markdown_representation(x))

show(io::IO, ::MIME"text/html", x::Fancy) =
    println(io, html(markdown_representation(x)))

```

It is a pity that I have to hijack `display`, but could not find a workaround. Perhaps I should open an issue, defining a single method that just constructs a representation and prints _everywhere_ would be nice.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [December 13, 2017, 6:03pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/10 "2017-12-13T18:03:19Z")

</div>

> [@Tamas\_Papp](#):
>
> ```julia
> function Base.show(io::IO, mime, f::Foo)
> t = Markdown.Table([[string(f.x)]], [:c])
> m = Markdown.MD(t)
> show(io, mime, m)
> end
> 
> ```

You really only want to do this for MIME types where `MD` defines an output method, right? You could do:

```julia
for T in map(m -> m.sig.parameters[3], methods(show, (IO,MIME,Base.Markdown.MD)))
    @eval function Base.show(io::IO, mime::$T, f::Foo)
        t = Markdown.Table([[string(f.x)]], [:c])
        m = Markdown.MD(t)
        show(io, mime, m)
    end
end

```

to define pass-through methods for all MIME types supported by `MD`, which would also fix the ambiguity errors, right?

Hijacking `display` is a bad idea, not the least because it will only work for `display` calls, not for output to any other stream.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [December 13, 2017, 6:13pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/11 "2017-12-13T18:13:13Z")

</div>

> [@Tamas\_Papp](#):
>
> am I doing this the wrong way?

There is a method ambiguity because of the fallback `show` for `text/plain` (which calls the 2-argument `show(io, x)` by default). If you just define a `text/plain` method to resolve the ambiguity it works fine. (Currently, `display` prints method errors in a confusing way…would be good to fix this.)

The following works for me:

```julia
julia> struct Foo
           x
       end

julia> function myshow(io::IO, mime, f::Foo)
           t = Markdown.Table([[string(f.x)]], [:c])
           m = Markdown.MD(t)
           show(io, mime, m)
       end
myshow (generic function with 1 method)

julia> Base.show(io::IO, mime, f::Foo) = myshow(io, mime, f)

julia> Base.show(io::IO, mime::MIME"text/plain", f::Foo) = myshow(io, mime, f)

julia> Foo(3)
| 3 |
|:---:|

```

Probably you should also define

```julia
Base.mimewritable(mime::MIME, f::Foo) = mimewritable(mime, Markdown.md"")

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 13, 2017, 6:41pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/12 "2017-12-13T18:41:10Z")

</div>

This works (except for the last `show` definition, where you missed a `mime`), but ideally it would show the markdown rendered in the terminal. Currently the only way for this is to hijack `display`.

In any case, I think that the general point of this thread is that it would be great if one could define a method that renders a type into `Markdown`, and then relevant methods could display it in various media. This would just require a single method for universal pretty printing.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 13, 2017, 6:51pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/13 "2017-12-13T18:51:22Z")

</div>

This is a related issue:  
[https://github.com/JuliaLang/julia/issues/10009](https://github.com/JuliaLang/julia/issues/10009)

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [December 13, 2017, 6:56pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/14 "2017-12-13T18:56:42Z")

</div>

> [@Tamas\_Papp](#):
>
> but ideally it would show the markdown rendered in the terminal. Currently the only way for this is to hijack display.

What do you mean? It is using the markdown display in the terminal, as shown in my output

```julia
julia> Foo(3)
| 3 |
|:---:|

```

That is, the REPL is using the `Markdown.MD` type’s text/plain output. Isn’t that what you want?

> In any case, I think that the general point of this thread is that it would be great if one could define a method that renders a type into Markdown, and then relevant methods could display it in various media. This would just require a single method for universal pretty printing.

Isn’t that what my code does? Modulo an extra method to fix the ambiguity?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 13, 2017, 6:59pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/15 "2017-12-13T18:59:32Z")

</div>

> [@stevengj](#):
>
> That is, the REPL is using the Markdown.MD type’s text/plain output. Isn’t that what you want?

No, I want the nicely rendered output, even in the terminal. This is what it looks like for the example above with `Fancy`:  
 ![Screenshot_2017-12-13_19-58-39](https://global.discourse-cdn.com/julialang/original/3X/6/2/62e439229f84ddc82dea483d66db71486c1e3758.png)

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [December 13, 2017, 7:05pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/16 "2017-12-13T19:05:59Z")

</div>

> [@Tamas\_Papp](#):
>
> No, I want the nicely rendered output, even in the terminal.

Oh, I see the problem: the Base.Markdown module overrides `Base.display(d::Base.REPL.REPLDisplay, md::MD)`. Probably it shouldn’t do this: it should use the `IOContext` properties in `show` to decide whether to use terminal formatting codes (in the same way that we should use these properties to decide whether to print ANSI color codes).

(I’m guessing that this code for Markdown display pre-dates the whole `IOContext` stuff.)

Update: fixed by [#25067](https://github.com/JuliaLang/julia/pull/25067).

---

<div class="post-metadata">

**Author:** ![yakir12](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yakir12/32/297_2.png) [@yakir12](https://discourse.julialang.org/u/yakir12)\
**Post date:** [December 13, 2017, 10:00pm UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/17 "2017-12-13T22:00:16Z")

</div>

This touches an idea I’ve had for a while now (excuse me for rambling):

**Should we start thinking of recipes in other contexts?**

`Plots.jl`’s implementation of recipes is powerful because with almost no overhead you can tell `Plots.jl` how to plot your custom types _iff_ the user wants to. It’s like really functional and light weight glue. Another way of tackling this is adding some conditional code that connects your package with some other package with `Requires.jl` (but from what I’ve heard [from Tim holy] it’s not “precompile-friendly”).

I’ve seen multiple cases where the need to connect unique packages to a common output package exists. Instead of including all the possible outputs in the unique package, or vice versa, I keep thinking that a `Recipes.jl` would really do the trick.

Here are some examples to get you thinking:

1. AxisArrays/OffsetArrays → CSV: offsets, axis values, and names can be parsed into a csv when saving.
2. AxisArrays/OffsetArrays → Interpolations: the interpolation of an such an array should be automatically scaled/offset.
3. CategorialArrays → HDF5: With the sparse format of these arrays, the HDF5 save should also reflect that.
4. and all the `show` methods…

Ramble out.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [March 26, 2018, 12:52am UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/18 "2018-03-26T00:52:14Z")

</div>

> [@yakir12](#):
>
> Plots.jl’s implementation of recipes is powerful because with almost no overhead you can tell Plots.jl how to plot your custom types iff the user wants to. It’s like really functional and light weight glue. Another way of tackling this is adding some conditional code that connects your package with some other package with Requires.jl (but from what I’ve heard [from Tim holy] it’s not “precompile-friendly”).

The recipes do a lot more than that. Because (type) recipes are applied recursively until you get to some basic bottom types that have a representation, a (type) recipe just needs to output some type which may not have a representation. The DiffEq recipe says how a `sol` → `(t,u)`. The UnitfulPlots.jl recipe says how to strip the units. The DimensionalPlotRecipes.jl recipe says how to reduce complex numbers to multiple dimensions of floating point numbers. When someone does `plot(sol)` on a DiffEq solution where the state variables are complex numbers with units, then if the user has these packages (i.e. the correct `apply_recipe` dispatches), then the chain

`sol -> (t,u) -> unitless (t,u) -> floating point (t,real(u),imaginary(u))`

all by recusing the simple type handling dispatches which Plots.jl does until it gets something it can handle.

The power of this cannot be overblown. The “standard way” of extending plotting is to write functions. Someone in Python creates a number object with units and creates function that calls matplotlib internally. But this way of writing plotting functions doesn’t compose. DiffEq is able to write its conversion without having to know how the number type will convert, and the same for Unitful, etc.

The second part to Plots.jl is that it has series recipes which say how you should actually plot a primitive. So a bunch of arrays: how do you plot them? Bunch of lines? Scatter plot? These are the series recipes.

Having this in mind, a recipe system for `show` would be great! A lot of people would probably just implement a type recipe which converts their type to say a Markdown object, and then let’s that show do its work. @Tamas_Papp would implement a new series recipe for Markdown objects, and then have his type do a type recipe plus impose a downstream series choice. The show mechanism would have to pass along extra information about the downstream display (which it currently does, like whether it should be compact) that the recipes would use. The Markdown object isn’t the best example since it probably naturally recurses using a `show` to build the strings for inside of the cells, but there’s probably similarly complicated examples as the DiffEq plot which would apply here.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [March 26, 2018, 6:48am UTC](https://discourse.julialang.org/t/show-is-too-low-level/7531/19 "2018-03-26T06:48:16Z")

</div>

> [@ChrisRackauckas](#):
>
> @Tamas_Papp would implement a new series recipe for Markdown objects, and then have his type do a type recipe plus impose a downstream series choice

I am nowhere near that sophisticated 😉 Basically, I just want to minimize effort for implementing various output formats, and thought of going through Markdown (not the markup format in plain text, but the internal representation of the semantics) as a reasonable common way to do this.

Initially I thought that this should be in `Base`, now I think this is fine as a package. And `Markdown` may be too limiting.
