Dispatch differences between restricted and unrestricte type parameter

Why is the dispatch behavior different for the following codes? The only difference is that the type parameter T of the abstract type is restricted (<:Real) in the first snippet and unrestricted in the second snippet.

abstract type Abstract{T<:Real} end
struct Concrete{T} <: Abstract{T}
    Concrete(t) = new{typeof(t)}()
[Concrete(1), Concrete(1.0)] isa Vector{<:Abstract}                # false
[Concrete(1), Concrete(1.0)] isa Vector{<:Abstract{<:T}} where {T} # true

abstract type Abstract2{T} end
struct Concrete2{T} <: Abstract2{T}
    Concrete2(t) = new{typeof(t)}()
[Concrete2(1), Concrete2(1.0)] isa Vector{<:Abstract2}                # true
[Concrete2(1), Concrete2(1.0)] isa Vector{<:Abstract2{<:T}} where {T} # true
1 Like

The following may help you understand or further confuse you.

struct Concrete1R{T<:Real} <: Abstract{T}
    Concrete1R(t) = new{typeof(t)}()

[Concrete1R(1), Concrete1R(1.0)] isa Vector{<:Abstract}                # true
[Concrete1R(1), Concrete1R(1.0)] isa Vector{<:Abstract{<:T}} where {T} # true

It’s not dispatch, it’s the subtyping, due to the abstract type type parameter constraint not being inherited by its subtype:

julia> abstract type A{T<:Real} end

julia> struct U{T} <: A{T} end

julia> struct V{T<:Real} <: A{T} end

julia> Vector{U} <: Vector{<:A}

julia> Vector{V} <: Vector{<:A}

Thanks to both of you. I was calling it dispatch because in my original code came up when I tried to dispatch on Vector{<:Abstract} and it threw a MethodError.

I was wrongly assuming, that concrete types inherit the abstract type parameter constraint, which is why I put it there in the first place. So that I cannot construct concert subtypes violating that constraint.

Now it makes sense! Or at least I understand the rules, even though I still find them quite unintuitive. I’ll just omit the type restriction in my abstract type then.

1 Like

Added some labels to the Github issue so it wouldn’t be forgotten.

1 Like