Writing parameter dependent methods for abstract type

I want to write method for all subtypes of an abstract type which would make use of type parameter. Can you do that? Example, let us say we have concrete type MyType{T} and some method which needs T, like

struct MyType{T} end
Base.convert(::Type{MyType{T},::Type{S}) where {S,T} = MyType{promote_type(T,S)}

This works, but can you write it so that it works for all subtypes of some MyAbstractType? Something like (disfunctional)

abstract type MyAbstractType{T} end
Base.convert(::Type{C{T}},::Type{S}) where {S,T, C{T}<:MyAbstractType{T}} = C{promote_type(T,S)}

This seem to be decidable, but I do not know how to achieve it. Essentially, when I get as parameter type which is of form A = B{C} I need to have access to B and C separately. To get the same functionality I can just repeat definitions for all subtypes I make, but it made me curious.

C{T}<:MyAbstractType{T} asserts the subtype has 1 shared type parameter to specify, which is not true in general. It’s possible for subtypes to have fewer or more type parameters than a supertype:

struct A <: MyAbstractType{Int} end
struct B{U,V} <: MyAbstractType{V} end

A doesn’t share MyAbstractType’s parameter and isn’t even parametric, so while T could be assigned Int, there is no reasonable parametric type to assign C. Do we introduce a frequent source of UndefVarError for static parameters, or do we try to exclude A from dispatching to this method without breaking method specificity somehow? B has 2 parameters, and even if we match the obvious supertype parameter to the subtype by the name V within the definition, do we assign C to B requiring 2 input parameters or a partially specified B subtype to replace T? These don’t have satisfying or intuitive answers, especially when we generalize to the supertype having >1 type parameter. Static parameters represent entire types ::T or type parameters ::Vector{T}, not an unknown parametric type ::T{Int}.

That said, you’re not out of options. With the allowed ::Type{C} and C<:MyAbstractType{T}, a helper function like _stripparameter(::Type{<:MyType}) = MyType can map the concrete type C to whichever iterated union you prefer. That looks like ridiculous boilerplate, but you’d need nontrivial decisions for A or B.

Thanks! So essentially I need to make a convention that all subtypes are parametrised in the same way and then add additional helper function.