HaberdashPI
HaberdashPI

Reputation: 400

How can I define a julia macro that defines a macro?

I would have thought this would work:

macro meta_meta(x,y)
  :(macro $x(arg) :($($y) + $arg) end)
end

The expected behavior is that calling @meta_meta(f,2) should be equivalent to macro f(arg) :(2 + $arg) end

In other words:

julia> @meta_meta(f,2)
julia> @f(3)
5

Instead I get:

ERROR: syntax: invalid macro definition

I'm at a bit of a loss for how to proceed. I see that the expression tree for this macro is different from the one I get if I manually generate @f and examine its expression tree, and I've tried several iterations of @meta_meta, but I cannot figure out how to change my definition to get it working.

Upvotes: 5

Views: 731

Answers (1)

Fengyang Wang
Fengyang Wang

Reputation: 12051

Macro hygiene is a little finnicky when dealing with a quote inside a quote. Often I find the only way is to refuse macro hygiene entirely, and use gensym liberally to simulate it.

However in your reduced example, it's straightforward to just turn the inner quote into an Expr:

julia> macro meta_meta(x, y)
           :(macro $(esc(x))(arg) Expr(:call, :+, $(esc(y)), esc(arg)) end)
       end
@meta_meta (macro with 1 method)

julia> @meta_meta f 2
@f (macro with 1 method)

julia> @f 3
5

If things get more complicated, the approach I mentioned above involves turning off macro hygiene with esc. This means that we have to do the hygiene ourself, hence the gensym:

julia> macro meta_meta(x, y)
           arg = gensym()
           esc(:(macro $x($arg) :($$y + $$arg) end))
       end
@meta_meta (macro with 1 method)

julia> @meta_meta f 2
@f (macro with 1 method)

julia> @f 3
5

Upvotes: 6

Related Questions