Macro with variable argument list combined with closure

Hi,
i have a question about variable arguments in macros together with the scope of them.

Function count_test() should be a closure over the variable count and the lambda should be generated with a macro.

It should be possible to manipulate the count variable by sending a message (:inc or :dec), but the variable count will NOT change.

I did a macroexpand and the scope of count is NOT the Main, so the ESC was succesful. But anyway the count varibable do NOT change.

The example is from the book LetOverLambda Chapter 5.7.

Thanks

macro counter_macro(body...)
    return quote
        println("macro")
        (msg) ->
            begin
                if $(body[1])[1] == msg 
                    println("test inc")
                    $(esc(body[1]))[2] = $(esc(body[1]))[2] + 1  
                end
                if $(body[2])[1] == msg 
                    println("test dec")
                    $(esc(body[2]))[2] = $(esc(body[2]))[2] - 1 
                end
            $(esc(body[2]))[2]
        end
    end
end
function count_test()
    count = 0
    return (@counter_macro  [:inc count] [:dec count])
end

c2 = count_test()
c2(:inc)
c2(:inc)
c2(:inc)
c2(:dec)

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

Works perfekt. Thanks.
In the next step I will abstract away the if clauses, thats why I use a variable list.