Unexpected subtyping behavior <:

Hey everyone, could someone explain this subtyping behavior to me?
It does not seem intuitive to me.

Array{T where T} <: AbstractArray{T where T}  # true
Array{Array{T where T}} <: AbstractArray{Array{T where T}}  # also true

Array{Array{T where T}} <: AbstractArray{AbstractArray{T where T}}  # false

I’m wondering why the last expression evaluates to false.

Thanks in advance!

1 Like

To elaborate a bit more, if x <: y then in general Point{x} <: Point{y} may not always be true.

To begin, your first and second example work because Array <: AbstractArray and what you are passing inside {} are exactly the same i.e. in the second example, you are declaring x = Array{T where T} and y = Array{T where T} and then checking if Array{x} <: AbstractArray{y} is true. However, since x = y this is equivalent to checking if Array{x} <: AbstractArray{x} is true, which it is since Array <: AbstractArray. Similarly, for the first example. The first and second examples basically say that any Array can be represented as an AbstractArray.

julia> Array{Float64} <: AbstractArray{Float64}

Note that what you’ve done in your first and second examples is completely different from the following
because you are passing exactly the same types (unlike, subtypes which are passed below) inside {}.

julia> Float64 <: Real

julia> Array{Float64} <: AbstractArray{Real}

julia> Array{Float64} <: Array{Real}

Now, as may be apparent from the previous example, the behavior manifested by the first and second examples need not hold if what you are passing inside {} changes as it does in the third example. In fact, the third expression won’t even evaluate to true if I set Point = Array on both sides of the equation i.e.

julia> Array{T where T} <: AbstractArray{T where T}

julia> Array{Array{T where T}} <: Array{AbstractArray{T where T}}

This behavior is consistent with the documentation below.

Here is a more formal treatment of what’s meant by covariant, contravariant and invariant subtyping