# Printing of UnionAll types is misleading, as of Julia version 1.7

**URL:** <https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418>\
**Category:** Internals & Design\
**Tags:** parametric-types\
**Created:** [October 28, 2022, 2:45am UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418 "2022-10-28T02:45:33Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [October 28, 2022, 2:45am UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/1 "2022-10-28T02:45:33Z")

</div>

Printing of UnionAll types used to be honest:

## Julia 1.6

```julia
julia> struct A{asdf, qwer} end

julia> A{Int}
A{Int64, qwer} where qwer

julia> using StructArrays

julia> struct Foo
           x::Int
       end

julia> StructArray{Foo}
StructArray{Foo, N, C, I} where {N, C<:Union{Tuple, NamedTuple}, I}

```

But as of Julia version 1.7, printing of UnionAll types is misleading:

## Julia 1.7

```julia
julia> struct A{asdf, qwer} end

julia> A{Int}
A{Int64}

julia> using StructArrays

julia> struct Foo
           x::Int
       end

julia> StructArray{Foo}
StructArray{Foo}

```

The printing in 1.7+ could easily lead a programmer to believe that `A{Int}` and `StructArray{Foo}` are concrete types. In fact, [here’s a Discourse question](https://discourse.julialang.org/t/understanding-type-instability-related-to-structarrays/89413) where the misleading printing might have been a contributing factor to the mistake that was made.

## Question

Why was this change made to UnionAll printing?

## Footnote

I’m aware that `StructArray{Foo}` is equivalent to the long form:

```julia
julia> StructArray{Foo} == (StructArray{Foo, N, C, I} where {N, C<:Union{Tuple, NamedTuple}, I})
true

```

However, I think printing `StructArray{Foo}` instead of the long form just has too much potential for confusion and obfuscation.

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [October 28, 2022, 3:14pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/2 "2022-10-28T15:14:15Z")

</div>

Everyone’s always complaining that parametric type printing is too verbose and cryptic. So you make the printing of parametric types less verbose and closer to what a person would write. Then bam, what happens? That’s no good neither.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [October 28, 2022, 3:53pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/3 "2022-10-28T15:53:07Z")

</div>

I know you can’t please everyone, but I would err on the side of clarity rather than brevity. A common mistake that I see from beginners is not realizing that `Array{Int}` is an abstract type. This new parsimonious printing just exacerbates that problem.

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [October 28, 2022, 3:54pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/4 "2022-10-28T15:54:53Z")

</div>

One slightly subtle option would be printing a trailing comma if there are more type parameters. Ie `Vector{Int}` but `Array{Int,}`.

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [October 28, 2022, 4:45pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/5 "2022-10-28T16:45:50Z")

</div>

The trailing comma idea seems perfect to me. We need a cake-and-eat-it-too solution, where stack traces are still short but we can see type instability easily. `Array{Int,}` contains just as much information as `Array{Int,N} where N` with only one character added. We could even have `Array{,}`, and `SomeType{Int,,,,,,}` ? Those commas communicate a lot using only one character.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [October 28, 2022, 5:17pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/7 "2022-10-28T17:17:42Z")

</div>

I can see the other point of view, which is this:

Type parameters that are part of the interface for a package should be documented. Type parameters that are not documented are implementation details. Since the number of public type parameters is defined by the documentation, there is no obligation to print trailing type parameters. (Hopefully sane package authors will have all the public type parameters precede the private type parameters.)

For example, it is documented that `Array` has two type parameters, `T` and `N`. So if one has read the documentation, then one would know that `Array{T}` has one free parameter and is therefore a `UnionAll` abstract type.

However, without careful documentation, folks that are creating wrapper types might accidentally define a `struct` field with an abstract type. So, the `StructArray` docstring would probably need a note like the following:

> The type `StructArray{T, N}` with `T` and `N` fixed is an abstract type, because there are additional free type parameters that are considered implementation details.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [October 28, 2022, 6:01pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/8 "2022-10-28T18:01:05Z")

</div>

> [@Raf](#):
>
> We could even have `Array{,}`, and `SomeType{Int,,,,,,}`

Printed types are usually runnable code. `Array{Int,}` is runnable, but multiple trailing commas is not runnable:

```julia
julia> struct Foo{A, B, C} end

julia> Foo{Int,}
Foo{Int64}

julia> Foo{Int,,}
ERROR: syntax: unexpected ","

```

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [October 28, 2022, 6:05pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/9 "2022-10-28T18:05:34Z")

</div>

> [@CameronBieganek](#):
>
> Printed types are usually runnable code. `Array{Int,}` is runnable, but multiple trailing commas is not runnable

```jl
julia> struct Foo{A, B, C} end

julia> Foo{Int, #=...=#}
Foo{Int64}

```

Is another option. It’s a bit verbose, but at least the verbosity only scales O(1).

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [October 28, 2022, 6:35pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/10 "2022-10-28T18:35:01Z")

</div>

That makes sense, but I’m trying to workshop your problem without creating a problem for me, which is usually stack traces that are far too long. The idea is a some middle ground that is “honest” but doesn’t take characters.

@jar1 has a possible solution. I wonder what other middle ground ideas could work and also be runnable types.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [October 28, 2022, 6:49pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/11 "2022-10-28T18:49:38Z")

</div>

> [@Raf](#):
>
> I’m trying to workshop your problem without creating a problem for me, which is usually stack traces that are far too long.

Can you give an example? Off the top of my head, I’m not seeing how `UnionAll` printing affects stack traces. In the couple examples that I’ve tried, what gets printed is the concrete types of objects.

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [October 28, 2022, 7:51pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/12 "2022-10-28T19:51:57Z")

</div>

I don’t really know… I haven’t been following this much more than “there seems to be less junk in the REPL these days, thanks whoever did whatever that was”.

But generally printing `X} where X` is a waste of space when a comma would communicate nearly as much. Basically I just like the idea of a comma as an abbreviation of unspecified type parameters anywhere.

---

<div class="post-metadata">

**Author:** ![uje](https://avatars.discourse-cdn.com/v4/letter/u/5f9b8f/32.png) [@uje](https://discourse.julialang.org/u/uje)\
**Post date:** [October 28, 2022, 10:15pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/13 "2022-10-28T22:15:55Z")

</div>

While the `,` is a middle ground for this, IMHO, I think it will break some Julia concepts. For example, [under Noteworthy differences from R](https://docs.julialang.org/en/v1/manual/noteworthy-differences/#Noteworthy-differences-from-R), it states:

> Julia allows an optional trailing comma when that comma does not change the meaning of code.

Using `,` for `Foo{Int64,}` and then saying somewhere in the doc : **“this means there are still other types present in the DataType”,** I think it is breaking (in concepts) since users who won’t read that part of the docs will be open to surprises and then wonder and ask themselves what is actually happening:

```julia
julia> Foo{Int64,} == Foo{Int64}
true

```

As you pointed out @CameronBieganek

> Printed types are usually runnable code.

So it would make sense to just leave it as `Foo{Int64}`. However this isn’t a runnable code:

```julia
julia> Vector
Vector (alias for Array{T, 1} where T)

julia> Vector (alias for Array{T, 1} where T)
ERROR: syntax: space before "(" not allowed in "Vector (" at REPL[6]:1

```

So my suggestion is this: print it as `Foo{Int64}` but then provide `(Foo{Int64, ....})`. Using an example, it would be printed this way:

```julia
julia> struct Foo{A, B, C, D} end

julia> Foo{Int64}
Foo{Int64} (Foo{A, B, C, D})

julia> Foo{Int64, Int64}
Foo{Int64, Int64} (Foo{A, B, C, D})

julia> Foo{Int64, Int64, Int64}
Foo{Int64, Int64, Int64} (Foo{A, B, C, D})

julia> Foo{Int64, Int64, Int64, Int64}
Foo{Int64, Int64, Int64, Int64}

```

The `(Foo{A, B, C, D})` part will be printed in dull colour just as `(alias for Array{T, 1} where T)` is; once it’s that way, then it doesn’t have to be a runnable code. This would let ANYONE in the world easily see what number of types are actually need to make it a concrete type.

An alternative would be to print it out this way:

```julia
julia> struct Foo{A, B, C, D} end

julia> Foo{Int64}
Foo{Int64} (Foo{..., B, C, D})

julia> Foo{Int64, Int64}
Foo{Int64, Int64} (Foo{..., C, D})

julia> Foo{Int64, Int64, Int64}
Foo{Int64, Int64, Int64} (Foo{..., D})

julia> Foo{Int64, Int64, Int64, Int64}
Foo{Int64, Int64, Int64, Int64}

```

And users can easily see the number of types that has been called and the number left. I think the first is good though. But anyways, I don’t think the `,` is great. If for anything, it won’t solve this issue in an elegant way and it would raise more confusions.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [October 29, 2022, 1:31am UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/14 "2022-10-29T01:31:14Z")

</div>

As far as I can tell, printing of `UnionAll` types doesn’t come into play in stacktraces, it only comes into play when you are inspecting a type in the REPL. And generally if you are inspecting a type in the REPL, you want as much information about the type as you can get. Thus, it seems to me that the old printing behavior is still the best.

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [October 29, 2022, 2:02am UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/15 "2022-10-29T02:02:50Z")

</div>

Type signatures are printed in stack traces.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [October 29, 2022, 4:31am UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/16 "2022-10-29T04:31:12Z")

</div>

This is my best attempt to get `UnionAll` printing to show up in a stacktrace on Julia 1.6:

```julia
julia> struct Foo{A, B} end

julia> struct Bar end

julia> asdf(::Type{Bar}) = 1
asdf (generic function with 1 method)

julia> qwer(x) = asdf(x)
qwer (generic function with 1 method)

julia> qwer(Foo{Int})
ERROR: MethodError: no method matching asdf(::Type{Foo{Int64, B} where B})
Closest candidates are:
  asdf(::Type{Bar}) at REPL[3]:1
Stacktrace:
 [1] qwer(x::Type)
   @ Main ./REPL[4]:1
 [2] top-level scope
   @ REPL[5]:1

```

This is an uncommon scenario where you pass a `UnionAll` type (not an instance) as an argument to a function. But even in this example the `UnionAll` printing only shows up in the `MethodError`, not in the stacktrace. For other scenarios, you see `UnionAll` printing in the `Closest candidates are:` section, but not in the stacktrace.

This doesn’t print a `UnionAll` in the stacktrace either:

```julia
julia> foo(x) = 1 + x
foo (generic function with 1 method)

julia> foo(Foo{Int})
ERROR: MethodError: no method matching +(::Int64, ::Type{Foo{Int64, B} where B})
Closest candidates are:
  +(::Any, ::Any, ::Any, ::Any...) at operators.jl:560
  +(::T, ::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} at int.jl:87
  +(::Union{Int16, Int32, Int64, Int8}, ::BigInt) at gmp.jl:534
  ...
Stacktrace:
 [1] foo(x::Type)
   @ Main ./REPL[6]:1
 [2] top-level scope
   @ REPL[7]:1

```

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [August 10, 2023, 8:31pm UTC](https://discourse.julialang.org/t/printing-of-unionall-types-is-misleading-as-of-julia-version-1-7/89418/17 "2023-08-10T20:31:34Z")

</div>

Here’s another thread where the new printing of UnionAll types is a hindrance to understanding:

> [@Partially parametrized external constructor of a parametric type](https://discourse.julialang.org/t/partially-parametrized-external-constructor-of-a-parametric-type/102687):
>
> Define a parametric type with parameters S and T (S is intended to be an integer, and T is intended to be a floating-point number): struct FooBar{S,T} x::S y::T FooBar{S,T}(x,y) where {S,T} = new(x,y) end Define an external constructor with only the second parameter T: FooBar{T}(y) where {T} = FooBar{Int,T}(0,y) The external constructor works: julia\> FooBar{Float64}(1.0) FooBar{Int64, Float64}(0, 1.0) Now, define the same parametric type and external constructor, except that S …

It’s a little disappointing that I had to go back to an old version of Julia in order to explain a concept.
