Macro with variable argument list combined with closure

In your if blocks, when you write

$(esc(body[1]))[2] = $(esc(body[1]))[2] + 1

this is macroexpanded to

([:inc count])[2] = ([:inc, count])[2] + 1

which is a setindex! expression on an array, not a lexical rebinding.

I would instead write this as

macro counter_macro(body...)
    quote
        println("macro")
        (msg) ->
            begin
                if $(body[1].args[1]) == msg 
                    println("test inc")
                    $(esc(body[1].args[2])) = $(esc(body[1].args[2])) + 1  
                end
                if $(body[2].args[1]) == msg 
                    println("test dec")
                    $(esc(body[2].args[2])) =  $(esc(body[1].args[2])) - 1 
                end
            $(esc(body[2].args[2]))
        end
    end
end

(Well, actually I also wouldn’t use a variable argument list since you clearly only actually support two arguments, and have a pre-defined expectation for what sort of structure you want those arguments to have, but I guess that’s another topic)

With the above changes, I get

julia> c = let count = 0
           @counter_macro [:inc count] [:dec count]
       end
macro
#10 (generic function with 1 method)

julia> c(:inc)
test inc
1

julia> c(:inc)
test inc
2

julia> c(:dec)
test dec
1

julia> c(:dec)
test dec
0
1 Like