Confused about metaprogramming: macros

I have started to use Metaprogramming now. While my code is running, I noticed that there are some things that I do not understand. My understanding is that at the simplest level macros return an expression which is then evaluated. So, I tried to define a variable b and set its value to one using a macro:

macro test(x)
quote
b=1
end
end

When I then run in REPL:

b

Then I get an error message:

ERROR: UndefVarError: b not defined

The macro seems to return 1.

On the other hand if I type,

eval(Meta.parse(“b=1”))

Then b is defined in the REPL.

I would have expected the macro and the eval call to have the same effect. Apparently, there is something I am not understanding here…

Thanks!

Variables defined through macro get mangled so that they don’t accidentally overwrite other variables in the same context. You may use escaping with esc(b) within macro if you want to expose the name(s).

4 Likes

You can use esc to avoid the hygiene pass for certain variables or the entire expression.

julia> macro test()
           quote
               $(esc(:b))=1; nothing
           end
       end
@test (macro with 1 method)

julia> b
ERROR: UndefVarError: b not defined

julia> @test

julia> b
1

When working with macros, @macroexpand is your friend:

julia> @macroexpand @test
quote
    #= REPL[1]:3 =#
    b = 1
    #= REPL[1]:3 =#
    Main.nothing
end

julia> macro test2()
           quote
               b=1; nothing
           end
       end
@test2 (macro with 1 method)

julia> @macroexpand @test2
quote
    #= REPL[6]:3 =#
    var"#17#b" = 1
    #= REPL[6]:3 =#
    Main.nothing
end

Notice the mangled name of b in @test2.

Note that the number in the gensymed variable increases each time. Seeing 17 above, we can infer that it’ll be 18 next time:

julia> @test2

julia> var"#18#b"
1
5 Likes

Oh, thank you!

I actually had suspected this before and tried the esc… but not the correct way, which is why it did not work!

I start to understand this now!