Hide long types in error output for better readability

I am new to Julia and I like the language, but one thing I don’t like is the error messages. They are often a big block of mostly incomprehensible text which I suspect might scare some developers away. This is largely due to very complicated type definitions.
I’m not saying that we should remove information just format it nicer, maybe provide some aliases that can be expanded later.

Just look at this cutout of an error message I recently got (from Query.jl): (You could of course blame the developers for making so complicated types, but…)

DataValues.DataValueException()

Stacktrace:
DataValues.DataValueException()
[15] println(::Base.TTY, ::QueryOperators.EnumerableMap{NamedTuple{(:source, :timestamp, :val),Tuple{QueryOperators.GroupColumnArrayView{DataValues.DataValue{String},Grouping{Array{DataValues.DataValue{String},1},NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}}},:psr_meaning},QueryOperators.GroupColumnArrayView{String,Grouping{Array{DataValues.DataValue{String},1},NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}}},:timestamp},Int64}},QueryOperators.EnumerableIterable{Grouping{Array{DataValues.DataValue{String},1},NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}}},QueryOperators.EnumerableGroupBy{Grouping{Array{DataValues.DataValue{String},1},NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}}},Array{DataValues.DataValue{String},1},NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}},QueryOperators.EnumerableIterable{NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}},Tables.DataValueRowIterator{NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}},Tables.RowIterator{NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{Array{String,1},Array{String,1},Array{String,1},Array{Union{Missing, String},1},Array{Union{Missing, String},1},Array{Union{Missing, Float64},1}}}}}},var"#451#461",var"#452#462"}},var"#454#464"{var"#448#458"}}) at ./strings/io.jl:73
 [16] println(::QueryOperators.EnumerableMap{NamedTuple{(:source, :timestamp, :val),Tuple{QueryOperators.GroupColumnArrayView{DataValues.DataValue{String},Grouping{Array{DataValues.DataValue{String},1},NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}}},:psr_meaning},QueryOperators.GroupColumnArrayView{String,Grouping{Array{DataValues.DataValue{String},1},NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}}},:timestamp},Int64}},QueryOperators.EnumerableIterable{Grouping{Array{DataValues.DataValue{String},1},NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}}},QueryOperators.EnumerableGroupBy{Grouping{Array{DataValues.DataValue{String},1},NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}}},Array{DataValues.DataValue{String},1},NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}},QueryOperators.EnumerableIterable{NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}},Tables.DataValueRowIterator{NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{String,String,String,DataValues.DataValue{String},DataValues.DataValue{String},DataValues.DataValue{Float64}}},Tables.RowIterator{NamedTuple{(:timestamp, :market, :type, :psr_meaning, :quantity, :amount),Tuple{Array{String,1},Array{String,1},Array{String,1},Array{Union{Missing, String},1},Array{Union{Missing, String},1},Array{Union{Missing, Float64},1}}}}}},var"#451#461",var"#452#462"}},var"#454#464"{var"#448#458"}}) at ./coreio.jl:4

Inside a small terminal window this is hopeless.

So maybe we could use something like BigLongTypeAlias (automatically) and then expand this at some other place like a type tree/graph. Also there could be a function in the repl that would collapse/hide these long types.

Does anyone agree? I think this can be a huge improvement to the Julia user experience.

(Please excuse me if this has been a topic before, but I couldn’t find anything.)

1 Like

There have been some mentions of this before. I remember reading them, but Discourse’s search engine isn’t always the best…

Here’s one I found though: https://discourse.julialang.org/t/pretty-print-of-type/19555 It at least describes how this might be fixed for individual types, though the fix is not exactly “generally recommended”.

Some packages are worse than others — Query.jls are famously Byzantine. Sometimes I think a little expandable tree would be great, to save some scrolling.

For any one type I think you can overload the relevant show method, and print something abbreviated. Tracker.jl does something like this:

julia> param(rand(3)) |> typeof
TrackedArray{…,Array{Float64,1}}

Stack traces contain a lot of useful information. While it is natural that understanding them requires some familiarity with the language, they are invaluable for debugging and getting help — you can just copy and paste them, and convey a ton of useful information.

Because of this, I would suggest that you just ignore the details until you are more familiar with Julia, and gradually develop the skills to interpret stack traces.

You should perhaps use Julia inside some programming editor that helps with navigation. Eg in Emacs I find it very easy to navigate/highlight matching {}s and similar in REPL output. I suspect other IDEs have similar features.

The occasionally complex nested parametric types just reflect the complexity of Julia’s parametric type system. Combined with multiple dispatch and AOT compilation, this is one of the key ingredients that make Julia the powerful language it is.