Why does (AbstractArray{Union{Number, T}} where T <: Nothing) != (AbstractArray{Union{Number, Nothing}})

In the search to find something that would match an Vector{Union{MyType,Nothing}} and Vector{MyType} but not Vector{Nothing}.
I found the signature that works is Vector{Union{MyType,T}} where T<:Nothing, but now I need to understand why.

Because apparently:

true ==  (AbstractArray{Union{Number, Nothing}})  <: (AbstractArray{Union{Number, T}} where T <: Nothing) 
false ==  (AbstractArray{Union{Number, Nothing}})  :> (AbstractArray{Union{Number, T}} where T <: Nothing) 

but strangely if you simplify further

true ==  Union{Number, Nothing}  <: (Union{Number, T} where T <: Nothing) 
true ==  Union{Number, Nothing}  :> (Union{Number, T} where T <: Nothing) 
true ==  Union{Number, Nothing}  == (Union{Number, T} where T <: Nothing) 

Is there any easy way to expalain why that happens?

The key is that your T can match anything that’s a subtype of Nothing. Now while it may feel like that should just be Nothing itself and nothing more, it also includes the “bottom” type — Union{}.

julia> Union{} <: (T where T <: Nothing)
true

Thus it’s not just substituting in a Nothing where that T goes — a Union{} could go there, too. And if that happens, we end up with simply:

julia> Union{Number, Union{}}
Number

So that means that we have an additional subtype here:

julia> AbstractArray{Number} <: AbstractArray{Union{Number, T}} where T <: Nothing
true
4 Likes