[Probably] A Naive Macro Interpolation Doubt

Hellos,

All that I want to do is a basic a macro to access some arrays. Here is the working code with expressions:

# works
let
    N = 5
    a = zeros(N)
    b = collect(1:N)
    myexp = quote
        for i = 1:$N
            $a[i] = 2*$b[i]
        end
    end
    eval(myexp)
    a
end

Here my attempt to make a macro

# i wish it could work
macro myLoop(a, b, N)
    quote
        a, b, N = esc.((a,b,N))
        for i = 1:$N
            $a[i] = 2*$b[i]
        end
    end
end
function mytest()
    N = 7
    a = zeros(N)
    b = collect(1:N)
    @myLoop a b N
    a
end
mytest()

I got an error that N does not exist:

ERROR: UndefVarError: N not defined
Stacktrace:
 [1] mytest()
   @ Main ./Untitled-1:19
 [2] top-level scope
   @ Untitled-1:31

I’m sure I’m doing a very basic mistake, but I can’t figure out looking at examples on discourse.

Help please :pray:

Here’s the problem–your macro is producing code which tries to call the esc function on some local variable named N which doesn’t exist. esc is just like any other function–it doesn’t know that you actually want to interpolate the value of N (that’s what the $ syntax is for).

Here’s one version that works:

julia> macro myLoop(a, b, N)
           quote
               for i = 1:$(esc(N))
                   $(esc(a))[i] = 2*$(esc(b))[i]
               end
           end
       end

or, if you prefer:

julia> macro myLoop(a, b, N)
           quote
               a, b, N = ($(esc(a)), $(esc(b)), $(esc(N)))
               for i = 1:N
                   a[i] = 2*b[i]
               end
           end
       end

1 Like

A simpler change is to lift the esc calls out of the quote

macro myLoop(a, b, N)
    a, b, N = esc.((a,b,N))
    quote
        for i = 1:$N
            $a[i] = 2*$b[i]
        end
    end
end
2 Likes