Units, errors and type display

Do show methods change the types in error output? I couldn’t get that to work, I don’t really understand how it works

I’m also a big fan of Unitful. I thing units should get a first class treatment (may be as a part of stdlib) in the Julia ecosystem.
Nowadays using units e.g. for both values and axes of AxisArrays.jl ends up producing a pretty complex type output.
Just think of the readability of a simple StepRange from 1 to 100 seconds.

using Unitful
typeof(1u"s" : 1u"s" : 100u"s")

StepRange{Unitful.Quantity{Int64,Unitful.Dimensions{(Unitful.Dimension{:Time}
(1//1),)},Unitful.FreeUnits{(Unitful.Unit{:Second,Unitful.Dimensions{(Unitful.Dimension{:Time}(1//1),)}}(0,
1//1),),Unitful.Dimensions{(Unitful.Dimension{:Time}
(1//1),)}}},Unitful.Quantity{Int64,Unitful.Dimensions{(Unitful.Dimension{:Time}
(1//1),)},Unitful.FreeUnits{(Unitful.Unit{:Second,Unitful.Dimensions{(Unitful.Dimension{:Time}(1//1),)}}(0, 
1//1),),Unitful.Dimensions{(Unitful.Dimension{:Time}(1//1),)}}}}

We definitively need a way for introducing a type alias for error outputs or printing human readable type info. In my code I define many const statements like this

const QAxis{Name, Dim} = Axis{Name, AX} where AX <: AbstractArray{Q} where Q <: Dim
const TimeAxis = QAxis{S, Unitful.Time} where S;
const TimeArray{T, N, V} = AxisArray{T, N, V, <:Tuple{TimeAxis, Vararg{Axis}}};

and I would be very happy if I could see similar simplified view on the REPL error messages and stacktraces.

Yeah Unitful is pretty central for me too. There’s an issue tracking this now:

Wow, that’s big. I thought it had something to do with the interplay between StepRange and units, but even this is gigantic:

julia> typeof(1u"s")
Unitful.Quantity{Int64,Unitful.Dimensions{(Unitful.Dimension{:Time}
(1//1),)},Unitful.FreeUnits{(Unitful.Unit{:Second,Unitful.Dimensions{(Unitful.Dimension{:Time}(1//1),)}}(0, 
1//1),),Unitful.Dimensions{(Unitful.Dimension{:Time}(1//1),)}}}

This must be the simplest thing you can define with Unitful, and yet it has a type signature like that. I would have naively expected something like this:

Unitful.Quantity{Int64, Unitful.Dimension{:Time}, Unitful.Unit{:Second}}

Edit: Actually, I was sort of expecting

Unitful.Quantity{Int64, Unitful.Unit{:Second}}

so this is clearly a lot more complex than I imagined.

One of the problems with type parameters in Julia is that they can be both part of the (public) API of a type, but also just implementation detail. Say for Array{Int,1}, I’d say that both parameters are part of the public API, so it is good to see them. However, for other types, e.g. this struct holding a bunch of parameters:

struct Foo{F<:Function}
  ...
  fn:F 
end

Here the F is not part of the API (the type parameter is only needed for performance). Of course there are gray areas too.

So, somehow separating the public vs non-public and prioritizing the printing of the public ones could also help with this issue.

3 Likes

That’s an interesting point. When typing

typeof(1u"s":1u"s":100u"s")

I think that this is what I’d like to see:

StepRange{Unitful.Quantity{Int64, :Second}}

That looks great. You could strip out the Unitful.

StepRange{Quantity{Int64, :Second}}

But I think having :Second would require a customised type printing mechanism, as there’s no sensible way of choosing between :Time, :Second, Dimensions, FreeUnits etc…

Yes, this was not thought through, and isn’t a serious proposition. Just firing from the hip. I expect Unitful does a lot of extra stuff that requires more complexity.

This should actually be quite simple to get working in the REPL as well, afaict. I’m prototyping something based on TerminalMenus.jl right now, which is part of the REPL stdlib anyways.

Sneak peak:
terminalmenus

Still a couple of things to figure out, but seems like a good idea overall imho.

7 Likes