# Union vs. <: AbstractType

This is not really a question, because I know how to use them, but the notation still makes me confuse every time.

In summary, I feel that the notation `Vector{T} where T <: AbstractType` suggests that a single concrete type `T` is to be considered, while `Union{T1,T2}` suggests that both should be considered. The behavior is exactly the opposite:

``````julia> abstract type A end

julia> struct A1 <: A x end

julia> struct A2 <: A x end

julia> f(x::Vector{T}) where T <: A = 1
f (generic function with 1 method)

julia> g(x::Vector{Union{A1,A2}}) = 2
g (generic function with 1 method)

julia> x = [ A1(1), A2(1) ]
2-element Array{A,1}:
A1(1)
A2(1)

julia> f(x)
1

julia> g(x)
ERROR: MethodError: no method matching g(::Array{A,1})
Closest candidates are:
g(::Array{Union{A1, A2},1}) at REPL:1
Stacktrace:
 top-level scope at REPL:1

julia>
``````

Perhaps understanding more the inner implementation of `Union` makes that more intuitive? Any didactic comment on this behavior is appreciated.

2 Likes

I think the confusion is not between `Union` vs `abstract type`.
but between `Vector{Foo}` and `Vector{T} where T<:Foo`

You want to write

``````g(x::Vector{T)  where T<:Union{A1,A2}} = 2
``````

to get the behavour that relates to `<:AbstractType`.
though that still won’t actually match because its on homogenious Vector of just one of those types.
(but still that is the matching behavour)

This is because types parameters* like `Bar{Foo}` are about exactly that type. So if your array was specifically types as a Union, (or as a `Abstract{A}` then it would match.
(which you can do via `Union{A1, A2}[ A1(1), A2(1) ]`, but that by default `[ A1(1), A2(1) ]` would be a `Vector{A}` because it promote to the abstract type; rather than to a `Union`)
But `Bar{T} where T<:Foo` is anything that is a particular subtype.

(*except in tuples)

Aside:
a shorthand for `Vector{T} where T<:Foo` is `Vector{<:Foo}`

3 Likes

More generally, this comes down to invariance of type parameters. I think the manual does a great job at explaining this, if you want a detailed explanation:

4 Likes

Yes, that helps a little. Still, from intuition I would guess that this case would be equivalent to `T<:A` if `A1` and `A2` are the only subtypes of `A`. But that at least gives a practical reason to differentiate both things.

I think the major confusion I have is with the word `Union`. Seems like it should be `OneOf` , `Xor`, something like that.

Seems like in the version of Julia @lmiq uses, a `[ A1(1), A2(1) ]` is already a more tight type (`Array{A,1}`). Not that this changes anything about your reasoning.

Yeah that was a mistake on my part.
Fixed
That is also the case on julia 1.0.5
TIL

Which does make another another aspect, of confusion.
That the type of `[A1(1), A2(1)]` is promotes to the common abstract type.
not to the `Union` of the types.

I have never made use of this,
In the rare occations that I am dealing with a hetrogeniously types array, I tend to be specific.
and here i would write either.
`A[A1(1), A2(1)]`
or
`Union{A1, A2}[A1(1), A2(1)]`

which repectively match
`f1(::Vector{A})` (or the wider `f2(::Vector{<:A})`
and
`g1(::Vector{Union{A1,A2})` (or the wider
`g1(::Vector{<:Union{A1,A2})` or even `f2` from above.)

1 Like

It is important to realize that `Union{A1,A2}` is a subtype of, but importantly not equivalent to `A`. This is quite important for a lot of aspects of type inference, since treating abstract types as just unions of a finite number of types would mean that a lot of precompiled methods would need to be invalidated and recompiled every time you subtype an abstract type. Since you can even consider `Any` an abstract type, this could in the worst case force Julia to recompile pretty much all generic functions every time you define a new type.

6 Likes

Ah, now I got it! (I knew it was worthwhile asking things here). My confusion was mixing the definition of the function with the type to which the vector is promoted when it is created!

My confusion got solved when you showed me this example:

``````x = Union{A1,A2}[A1(1),A2(2)]
``````

and that of course works with the function `g` above.

Also, it makes sense that `A` is a supertype of the `Union` type, and now it makes sense that the vector that was promoted to `A[A1(1),A2(2)]` does not work on that same `g`.

Thank you very much.

2 Likes