Error with `show` for abbreviated type printing

Let’s say I have a type with an abbreviated show method for the type itself (for shortened stacktraces).

struct Blarg{X,Y}
    x::X
    y::Y
end

Base.show(io::IO, ::Type{Blarg{X,Y}}) where {X,Y} = print(io, "Blarg{$X,...}")
Base.show(io::IO, ::MIME"text/plain", ::Type{Blarg{X,Y}}) where {X,Y} = print(io, "Blarg{$X,$Y}")

Now if I define a function that uses type parameter annotations, I get an error. What is happening here? Why does it say X not defined?

julia> f(blarg::Blarg{X,Y}) where {X,Y} = blarg.x + blarg.y
f (generic function with 1 method)

julia> methods(f)
# 1 method for generic function "f":
[1] Error showing value of type Base.MethodList:
ERROR: UndefVarError: X not defined
Stacktrace:
  [1] show(io::IOContext{IOBuffer}, #unused#::
SYSTEM (REPL): showing an error caused an error
ERROR: UndefVarError: X not defined
Stacktrace:
  [1] show(io::IOContext{IOBuffer}, #unused#::
SYSTEM (REPL): caught exception of type UndefVarError while trying to handle a nested exception; giving up

Note that if the function definition doesn’t use type parameter annotations, there isn’t a problem:

julia> new_f(blarg::Blarg) = blarg.x
new_f (generic function with 1 method)

julia> methods(new_f)
# 1 method for generic function "new_f":
[1] new_f(blarg::Blarg) in Main at REPL[6]:1

use

::Blarg{X,Y}

instead of Type{Blarg{X,Y}} and it works (I don’t know exactly what that Type could be doing there, but I didn’t use it for a similar function before).

The Type is important here. I’m not trying to control the printing for the instance of the type, I’m trying to control the printing for the type itself when it’s inside of stacktraces.

1 Like

It is generally recommended not to overload show for Types. E.g. https://github.com/JuliaLang/julia/issues/22363#issuecomment-799984810 and These stack traces are out of control - #5 by cstjean
There has been discussion about how to handle this though (such as the discourse thread above). For now, there doesn’t seem to be a satisfying solution.

1 Like

I think this is probably Dispatching on the result of unwrap_unionall seems weird? and the answer is you want to use the new type aliasing stuff in Julia 1.6 rather than custom overloaded show.

3 Likes

@tomerarnon Ah, that’s unfortunate. The combination of DifferentialEquations.jl’s type parameterizations and ComponentArrays.jl’s value-as-type axis types make stack traces pretty useless without some type of abridged printing.

@marius311 Thanks for the tip on the aliasing solution, I’ll have to check that out. Hopefully it offers some distinction between MIME types because it’s important to be able to see a fully-printed type when calling typeof directly. This wouldn’t be a strict aliasing situation.

Never played with it so don’t know if it does, but if it doesn’t, another option is to use @isdefined in your custom show to avoid errors. You’ll have to play with in what scenarios things come in as defined or not, its a bit of a mystery to me still.

You can overload the internal function which is used by stacktrace printing. No guarantee this will keep working, but it ought not to change anything else, like methods(f), at the moment:

julia> function Base.print_type_stacktrace(io, type; color=:normal)
           str = first(sprint(show, type, context=io), 20)  # <-- only change, could add ... etc.
           i = findfirst('{', str)
           if isnothing(i) || !get(io, :backtrace, false)::Bool
               printstyled(io, str; color=color)
           else
               printstyled(io, str[1:prevind(str,i)]; color=color)
               printstyled(io, str[i:end]; color=:light_black)
           end
       end

julia> map(+, (((((1,),),),),))
ERROR: MethodError: ... 
Stacktrace:
 [1] map(f::typeof(+), t::Tuple{Tuple{Tuple{Tu)  # <-- trimmed here
   @ Base ./tuple.jl:213

That looks like the way to go. Thank you!