I’d like to simplify the following code so that there is only one method for multiplication that applies to all subtypes of AbstractTerm
abstract type AbstractTerm{T} end
struct ATerm{T} <: AbstractTerm{T}
data::T
end
struct BTerm{T} <: AbstractTerm{T}
data::T
end
function Base.:*(x::Number, aterm::ATerm{T}) where T
return ATerm{promote_type(typeof(x), T)}(x * aterm.data)
end
function Base.:*(x::Number, bterm::BTerm{T}) where T
return BTerm{promote_type(typeof(x), T)}(x * bterm.data)
end
a = ATerm(3)
println(4.1 * a)
I first had this method, instead of two,
function Base.:*(x::Number, term::AbstractTerm)
return typeof(term)(x * term.data)
end
But, that fails with an error for the example given. (Also, this is a simplified version of my actual type hierarchy)
What I’d like is something like ...term::T{V}) where {V, T<:AbstractTerm}. But, that, of course, is not valid Julia.
I find similar problems quite often, for example, I write identical constructors for all subtypes of an abstract type.
The method for ATerm in the OP will correctly give ATerm{Float64}(12.299999999999999). And likewise, for the method for BTerm. But, I can’t find a way to write a single method to do this for all AbstractTerm types. For each subtype, I have to write an identical method.
Oh, okay, now I see. I didn’t catch that the problem is the fact that your AbstractTerm’s parametric type (Int) doesn’t match the promoted type of the multiplication (Float64). You could strip the parametric type maybe but I think this is discouraged actually(?).
Nice trick, strip(::ATerm) = ATerm, I did not think of it. Defining strip for each type is economical, and I really want to avoid metaprogramming if possible. strip is not a super-elegant solution, but it may be the best. I would actually use it in many places.
strip(::T) where {T} = (isempty(T.parameters) ? T : T.name.wrapper)
seems to work. @btime does not do all the work at compile time, like it does for the hardcoded case above.
EDIT:
After testing further I find that the solution using T.wrapper is very slow compared to strip(::ATerm) = ATerm. I don’t recommend it.