If parametric types are invariant, why is Vector{Int} a subtype of AbstractVector{Int}?

If parametric types are invariant, why is Vector{Int} a subtype of AbstractVector{Int}?

julia> Vector{Int} <: AbstractVector{Int}
true

A somewhat more complete and self-contained example:

julia> abstract type Animal{T} end

julia> struct Cat{T} <: Animal{T} end

julia> Int <: Real
true

julia> Cat{Int} <: Animal{Real}
false

julia> Int <: Int
true

julia> Cat{Int} <: Animal{Int}
true

Since parametric types are supposed to be invariant, it seems like there should be no subtype relationship between Cat{Int} and Animal{Int}

1 Like

Your example is consistent with the first code block right? (this is not invariance or not, just abstract types mechanics)
And also consistent examples:

julia> Vector{Int} <: AbstractVector{Real}
false

julia> AbstractVector{Int} <: AbstractVector{Real}
false
2 Likes

Oh, right, I already forgot that type invariance means that
S <: T does not imply A{S} <: A{T}.

Whereas my example is B{T} <: A{T}

It still feels slightly inconsistent though… :thinking:

2 Likes

One way to think about it is that Vector{Real} is a concrete type while AbstractVector{Int} isn’t. As such Vector{Real} can’t be the supertype of anything, and AbstractVector{Int} can.

3 Likes

That’s a good point. Since AbstractVector{Int} is an abstract type, there must be something which is a subtype of it. Or rather, it must be possible for something to be a subtype of it.

2 Likes