Dispatch on parametric subtypes

I asked this question on the Julia Slack and got a reasonable fix but I am wondering if there is a better solution (specifically one that works entirely via multiple dispatch).

Suppose I have the following set up:

abstract type A{T} end
struct B{T} <: A{T} end
struct C{T} <: A{T} end
abstract type D{T} <: A{T} end
struct E{T} <: D{T} end
struct F{T} <: D{T} end

I want to define a promotion method so that all subtypes of A works like the following:

promote_rule(B{Float64}, B{Float32}) -> B{Float64}

and promote_rule between any two different subtypes (disregarding parameter, so like C{Int64} is different than E{Int64} but would be considered the same subtype as C{BigFloat}) should return an error.

At this point, I have the following solution:

function promote_rule(::Type{T}, ::Type{S}) where {U, V, T<:A{U}, S<:A{V}}
    if T == S
        N = typejoin(T, S)
        isabstracttype(N) ? error("incompatible types") : N{promote_type(U, V)}

however, this seems kinda hacky and maybe there are some edge cases where this works when it shouldn’t.

Is there a way to achieve this purely by multiple dispatch?

Just as context, the D abstract type and its subtypes were included because one suggestion on slack was to check if typejoin(T, S) was equal to A, in which case they would be incompatible types. Since there can be any arbitrary subtyping structure with abstract types thrown in, this approach wouldn’t work as we could not know what the lowest common abstract subtype ancestor is between two arbitrary types.

I don’t see why you think this is hacky; the computation has to happen somewhere. But I think you can get rid of the branch with

promote_rule(::Type{T}, ::Type{T}) where {T <: A} = T

Thanks for that snippet, I missed that simplification.

I guess hacky isn’t the best term. But I just figured there was a more elegant (one liner) solution that relies entirely on multiple dispatch to get the correct type where I don’t have to check if the typejoin is abstract.

Something like (this doesn’t work but it would have been cool if it did)

promote_rule(::Type{X{S}}, ::Type{Y{T}}) where {S, T, V<:A, X<:V, Y<:V} = V{promote_type(S,T)}
along with the snippet you provided to check for equal types.

1 Like