suppose I have two custom types A and B, and I wanna build a macro or a generated function that gives different behaviors according to the type, how could I do?
I tried the following:
struct A
data::Int
end
struct B
data::Int
end
a = A(1);
b = B(2);
is it possible to have a macro @f() or generated function f() that behaves like:
@f(a, x) or f(a, x) or @f(A, a, x) or @f(Type{A}, a, x)
@f(b, x) or f(b, x) or @f(B, b, x) or @f(Type{B}, b, x)
could you be nice enough to provide an example?
noted that I need to produce an expression rather than a value. If a normal function is used, inside it I got only the values but lost the calling expressions (e.g. a and x)
Maybe you want to have a generated function? But I’d also consider carefully whether a plain function can achieve what you want, metaprogramming should be used sparsely.
How complicated will those expressions be? And is some code inside a package or something the user will play with? If there is only a small number of such expressions that need to be generated, and all the inputs can be made available to one function, use a normal function with an if statement on the types of the inputs to change the code being run. If an if statement is not enough and you need fancier transformations, use a generated function.
@cscherrer had a similar problem before and probably has some good advice on this kind of problem.
struct A
data::Vector{Int}
end
struct B
data::Vector{Int}
end
a = A([1, 2]);
b = B([3, 4]);
I would like to have a macro @f() that returns an expression so that: @f(a, :x) gives :(a.data .+ x), and @f(b, :x) gives :(b.data .* x) (note the difference between .+ and .*)
if it is possible, then I could create nested expression, e.g. by: @f(a, @f(b, @f(a, :x) )
and get: :(a.data .+ (b.data .* (a.data .+ x) ) )
it’s difficult for me because:
as I need the name of the calling object (i.e. “a” in @f(a, :x) ), seems like I have to use a macro (not a function, not a generated function)
however, using macro I cannot infer the type of the object yet at the same time I want the behavior of the macro be different according to the type of the object…
I hope that can be achieved… maybe by macro calling another macro? or using strings?
the problem is: how to define @f()? help please… thanks
macro f(s, x)
ss = QuoteNode(s)
x = QuoteNode(x)
return quote
if $s isa A
:($$ss.data .+ $$x)
elseif $s isa B
:($$ss.data .* $$x)
else
:()
end
end
end
@f(a, x)
It doesn’t work with nested calls but maybe you can figure that out.
macro f(a, x)
:(f($(esc(a)), $(QuoteNode(a)), $(esc(x)))
end
And no using strings never solve anything. You can do strictly less and make it more complicated by using strings for metaprogramming. You should stop thinking about strings as a solution to any metaprogramming problems.
macro f(a, x)
aname = QuoteNode(a)
xname = QuoteNode(x)
quote
op = getop(typeof($a))
Expr(:call, op, $aname, $xname)
end
end
struct A
data::Int
end
struct B
data::Int
end
a = A(1);
b = B(2);
getop(::Type{A}) = +
getop(::Type{B}) = *
julia> @f a x
:((+)(a, x))
julia> @f b x
:((*)(b, x))