I see your point (that the discussion in the docs is focused on tuple types, not on union types). And as I said, I’m not entirely sure if this is intended or not (but more and more, based on the discussion below).
What exactly is the behavior you would expect?
I don’t get this. When we put the type parameter in the union and use the same parameter in all places, they better all be the same types over which we take the union. If we put no parameter that occurs in a tuple also in a invariant position (like Union{Tuple{T, T}, Vector}) within the union, the tuples are still restricted to the diagonal types and nothing unexpected happens.
Reading this type union
Union{Tuple{T,T}, Vector{T}} where T <: Number
I would always expect that T goes over all types which are subtypes of Number, and that there can be no T that appears in some of the Vector, but not in the Tuple and vice versa.
The only question in my mind is whether abstract types are included in the union or not.
Tuple{T,T} might suggest that only concrete types T <: Number are included, because in isolation, Tuple{T,T} where T<:Number allows only concrete types
- but
Vector{T} where T<:Number doesn’t exclude any abstract types for T in any other context (that I can think of right now)
I guess the question is, which behavior should take precedence over the other (the covariant parameters in the tuple types or the invariant ones). Reading the docs, it sounds to me like restricting the parameters for the tuples is treated as the exception, since it is required for matching function signatures in a meaningful way. But apart from that, the type parameters are always(?) invariant and unions range over all possible types of the parameter.
And more importantly, a (perhaps the?) basic property of unions should be that they grow as we add things.
With the current behavior
Vector{Number} <: Union{Tuple{T,T}, Vector{T}} where T <: Number
is true, which is what I would expect, since in any other situation Vector{T} where T <: Number allows abstract types for T. If adding Vector{T} to the union should enforce that T iterates over all subtypes of Number and is replaced in all three places where it occurs without changing the behavior of the tuple types then Vector{Number} <: Union{Tuple{T,T}, Vector{T}} where T <: Number should be false.
I think this would also imply that @jinfreedom’s example relation
(Union{Tuple{T,T}, Vector{T}} where T <: Number) <: (Union{Tuple{T,T}, Vector} where T <: Number)
would be true. This looks reasonable to me, but at the same time, the following would then be false (e.g. Number would be contained in the left, but not in the right):
(Union{T, Vector{T}} where T <: Number) <: (Union{T, Vector{T}, Tuple{T, T}} where T <: Number)
Should adding a Tuple to an arbitrary type union make the whole union smaller than just leaving it away?
The only other option I see would be that the tuple types are treated as they behave in isolation (only including concrete/diagonal types) and the vector type as well (including also abstract types), but that is just
Union{Tuple{T,T}, Vector{S}} where {S<:Number,T <: Number}
tl;dr If T should be the same T in all places in the union, we should rather “widen” the tuple types instead of restricting the other types to ensure (semantically) that unions grow when things are added.