In UnitTypes I need to restrict a method’s arguments to types that all subtype the same parent, though there are multiple families of parents and child types. Here’s an example:
abstract type AbstractMeasure end
abstract type AbstractLength <: AbstractMeasure end
struct MilliMeter <: AbstractLength
value::Number
end
finb = 1/1000
struct Inch <: AbstractLength
value::Number
end
finc = 0.0254
abstract type AbstractSound <: AbstractMeasure end
struct Growl <: AbstractSound
value::Number
end
struct Roar <: AbstractSound
value::Number
end
This yields the type tree:
# AbstractMeasure
# AbstractLength
# MilliMeter
# Inch
# AbstractSound
# Growl
# Roar
It makes sense to convert children of AbstractLength or AbstractSound, but converting a length into a sound should error. Since users are free to add additional measures under AbstractMeasure, I want to write the convert() once and have it be correctly restricted to measures of Length or Sound or etc while disallowing cross-conversion.
This would be nice but it fails:
function connie(::Type{T}, y::U) where {T<:AbstractMeasure, U<:supertype(T)}
return T(y.value * finc/finb)
end
@show connie(Growl, Inch(3.4))
Layering wheres should work,
function connie(::Type{T}, y::U) where {T<:N, U<:N} where N
return T(y.value * finc/finb)
end
@show connie(Growl, Inch(3.4))
but <: just reaches up to AbstractMeasure or Any instead of stopping at the immediate parent, AbstractLength.
code_warntype(connie)
# MethodInstance for connie(::Type{T}, ::U) where {N, T<:N, U<:N}
# from connie(::Type{T}, y::U) where {N, T<:N, U<:N} @ Main ..\typesDev.jl:29
# Static Parameters
# N <: Any
# T <: N
# U <: N
# Arguments
# #self#::Core.Const(Main.connie)
# _::Type{T} where {N, T<:N}
# y::Any
# Body::Any
# 1 ─ %1 = $(Expr(:static_parameter, 2))::Type{T} where {N, T<:N}
# │ %2 = Main.:/::Core.Const(/)
# │ %3 = Main.:*::Core.Const(*)
# │ %4 = Base.getproperty(y, :value)::Any
# │ %5 = Main.finc::Any
# │ %6 = (%3)(%4, %5)::Any
# │ %7 = Main.finb::Any
# │ %8 = (%2)(%6, %7)::Any
# │ %9 = (%1)(%8)::Any
# └── return %9
Similarly introducing another layer
function connie(::Type{T}, y::U) where {T<:N, U<:N} where N<:O where O<:AbstractMeasure
return T(y.value * finc/finb)
end
# MethodInstance for connie(::Type{T}, ::U) where {O<:AbstractMeasure, N<:O, T<:N, U<:N}
# from connie(::Type{T}, y::U) where {O<:AbstractMeasure, N<:O, T<:N, U<:N} @ Main w:\mechgits\dev\251013_unitTypesFaster\typesDev.jl:31
# Static Parameters
# O <: AbstractMeasure
# N <: O<:AbstractMeasure
# T <: N<:O<:AbstractMeasure
# U <: N<:O<:AbstractMeasure
# Arguments
# #self#::Core.Const(Main.connie)
# _::Type{T} where {O<:AbstractMeasure, N<:O, T<:N}
# y::AbstractMeasure
# Body::Any
# 1 ─ %1 = $(Expr(:static_parameter, 3))::Type{T} where {O<:AbstractMeasure, N<:O, T<:N}
# │ %2 = Main.:/::Core.Const(/)
# │ %3 = Main.:*::Core.Const(*)
# │ %4 = Base.getproperty(y, :value)::Any
# │ %5 = Main.finc::Any
# │ %6 = (%3)(%4, %5)::Any
# │ %7 = Main.finb::Any
# │ %8 = (%2)(%6, %7)::Any
# │ %9 = (%1)(%8)::Any
# └── return %9
makes N and O both AbstractMeasure while AbstractLength is again skipped over.
Is there any way to limit how far subtype reaches?
Thanks