I defined two abstract types, call them MyType and MyFrozenType{T<:MyType}.
Subtypes SpecificFrozenType <: MyFrozenStruct{SpecificType} are usually defined as SpecificFrozenType(x::SpecificType, v::Vector)
Now, I have a series of functions of the form f(y::SpecificFrozenType) for different inputs of subtypes of MyFrozenType. Is there a way to generically create functions of the form f(x::T, v::Vector) where T<: MyType = f(SpecificFrozenType(x, v)).
It’s tedious to rewrite this form for every concrete type, but I’m not sure what’s a more convenient way to do what I want to do.
Edit: I’m adding a MWE to clear up any confusion
abstract type MyType end
abstract type MyFrozenType{T<:MyType} end
struct Type1 <: MyType end
struct Type2 <: MyType end
struct Frozen1 <: MyFrozenType{Type1}
x::Type1
v::Vector{Float64}
end
struct Frozen2 <: MyFrozenType{Type2}
x::Type2
v::Vector{Float64}
end
f(y::Frozen1) = y.v
f(y::Frozen2) = 2 * y.v
f(x::Type1, v::Vector) = f(Frozen1(v))
f(x::Type2, v::Vector) = f(Frozen2(v))
What I’m trying to do is not have to type the last two lines for all the types I’m defining.
So I should have been clear that SpecificFrozenType also changes from function to function. Its the sole concrete type of MyFrozenType{SpecificType} for each SpecificType.
Also wouldn’t @eval affect performance somehow? I’ve always avoided using it in my code. I’m not familiar with metaprogramming at all.
Could you write up a MWE because it’s legitimately confusing which names are proper variables and which names are placeholders for multiple variables. Just write 2 types for the latter, your desired outcome won’t be too large to write out and it’d be clearer what we’re trying to shorten.
So with @eval I’m allowed interpolate from symbols into objects with that name? I was always under the impression that eval() should be used as a last resort.
What does the @eval x do exactly? Is it the same as eval(x)?
Excellent example. So you have somewhat of a mapping from Frozen_ to Type_ which you can leverage with typeof(Frozen1instance.x) or fieldtype(Frozen1, :x), though it’s not good to rely on the field being named x. However, you really want a mapping from Type_ to Frozen_, which can be done in methods like those f one by one, which a @eval loop can help with.
My suggestion would be to decouple those methods from f since it looks intended for specific algorithms, so you’d have to do it all over again for say, g. Which I guess you can do with another nested @eval loop, but the less we put on that the better.
# must specify one by one
f(y::Frozen1) = y.v
f(y::Frozen2) = 2 * y.v
# 1 method links the type mapping to f
f(x::MyType, v::Vector) = f(freezemytype(typeof(x))(v))
# one by one, pattern can @eval loop
freezemytype(::Type{Type1}) = Frozen1
freezemytype(::Type{Type2}) = Frozen2
# must specify one by one
g(y::Frozen1) = y.v
g(y::Frozen2) = y.v / 2
# 1 method links the type mapping to g
# can @eval loop over f, g, etc
g(x::MyType, v::Vector) = g(freezemytype(typeof(x))(v))
More like eval(:x). Both do Core.eval(@__MODULE__, :x), where @__MODULE__ expands to the enclosing module. @eval is just convenient to save writing(:()) or (quote end) around a source code block. If you work with a direct Expr instance instead of source code, use the function instead of the macro.