Note that using esc
like this will cause some interesting and hard-to-find bugs.
For example, the following code, using a function to implement map
, works fine:
julia> function map_function(f, y, x)
for j in 1:length(x)
y[j] = f(x[j])
end
end
map_function (generic function with 1 method)
julia> function do_stuff()
length = 3
x = [i for i in 1:length]
y = zeros(length)
map_function(sin, y, x)
@show y
end
do_stuff (generic function with 1 method)
julia> do_stuff()
y = [0.8414709848078965, 0.9092974268256817, 0.1411200080598672]
3-element Array{Float64,1}:
0.8414709848078965
0.9092974268256817
0.1411200080598672
But what happens if we use the esc()
-ed macro?
julia> macro map(f,y,x)
out = quote
for j = 1:length(x)
$y[j] = $f($x[j])
end
end
esc(out)
end
@map (macro with 1 method)
julia> function do_stuff()
length = 3
x = [i for i in 1:length]
y = zeros(length)
@map(sin, y, x)
@show y
end
do_stuff (generic function with 1 method)
julia> do_stuff()
ERROR: MethodError: objects of type Int64 are not callable
Stacktrace:
[1] macro expansion at ./REPL[7]:3 [inlined]
[2] do_stuff() at ./REPL[8]:5
Oops.
This is exactly why you should not blindly esc()
everything returned by the macro. What’s happening here is that there is a local variable called length
. By calling esc()
in the macro, you are saying “every symbol returned by this macro should refer to whatever symbol is in the scope where the macro was called”. That means that when the code returned by the macro does length(x)
, it tries to call the local variable length
as if it were a function.
Instead, you must only escape the inputs provided by the user (f
, y
, and x
in this case). Doing so fixes the issue:
julia> macro map(f,y,x)
quote
for j = 1:length($(esc(x)))
$(esc(y))[j] = $(esc(f))($(esc(x))[j])
end
end
end
@map (macro with 1 method)
julia> function do_stuff()
length = 3
x = [i for i in 1:length]
y = zeros(length)
@map(sin, y, x)
@show y
end
do_stuff (generic function with 1 method)
julia> do_stuff()
y = [0.8414709848078965, 0.9092974268256817, 0.1411200080598672]
3-element Array{Float64,1}:
0.8414709848078965
0.9092974268256817
0.1411200080598672
There’s no avoiding the fact that doing esc
correctly creates some extra noise, unfortunately. Subtleties of macro hygiene are one of many reasons to prefer writing functions unless you are doing something that can only be handled by a macro.