Macro: is it possible to receive a function and manipulate its arguments?

Hi. I’m a newbie of Julia macro.

I’d like to know whether it’s possible to get a function and manipulate its arguments by macro.
For example, something like this.

@mymacro func(a, b)   # == func(a+b, a-b)

Absolutely, the general way to go about writing macros is figure out what the expression tree looks like for the thing you’re trying to transform:

julia> ex = :(func(a,b))
:(func(a, b))

julia> dump(ex)
Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol func
    2: Symbol a
    3: Symbol b

then figure out how to transform that ex into the thing you want and put it inside the macro,

macro mymacro(ex)
    # for your simple example above:
    esc(:($(ex.args[1])($(ex.args[2])+$(ex.args[3]), $(ex.args[2])-$(ex.args[3]))))
end

then check it did the right thing:

julia> @macroexpand @mymacro func(a,b)
:(func(a + b, a - b))

The MacroTools package has some convenient tools for matching and transforming expressions which are almost always useful, that can make writing that even more concise / readable (in particular look at @capture).

Not sure if your real case is more complex, but if not, note that there’s ways to do your simple example which don’t involve macros (and if you don’t really need one, its almost always the right choice not to):

julia> func(a,b) = (a,b)
func (generic function with 1 method)

julia> sumdiff1(a, b) = (a+b, a-b)
sumdiff1 (generic function with 1 method)

julia> func(sumdiff1(1,2)...)
(3, -1)

or

julia> sumdiff2(f) = (a,b) -> f(a+b, a-b)
sumdiff2 (generic function with 1 method)

julia> sumdiff2(func)(1,2)
(3, -1)
6 Likes

Thanks a lot! It’s a great explanation.

One more question: Can I use a macro to predefine some values for a certain structure?
For example, I would like to set some values of struct A and reuse it (to avoid additional calculation) after the macro. Like this:

  • What I want to do
my_value(a::A) == 1
  • Assume that @value A is identical to what I want (Can a macro receive a type? I’m not sure)
@value A
a = A()
my_value(a)  # == 1