I am trying to create a variable inside a Julia macro, then return the variable to the REPL, the variable should also have the name I specified from the macro input argument. I have two tests, the first test function below seems working:
macro my_test(args)
var_call = add_variable(args)
return esc( quote $args = $var_call end)
end
function add_variable(name)
d = PhaseIndex(1)
var_ref = generic_dy_ref(d,2)
return var_ref
end
struct PhaseIndex
p::Int64
end
struct generic_dy_ref
phase::PhaseIndex
index::Int64
end
It is able to generate a variable in the REPL with the same name as the input argument.
julia> @my_test(x)
generic_dy_ref(PhaseIndex(1), 2)
julia> x
generic_dy_ref(PhaseIndex(1), 2)
In my own macro I require a function call which is enclosed by :(), however, adding :() seems to create error at the return line, here is the modified test macro and the returning error, it seems that the “var_call” cannot be evaluated in this way?
You’re double escaping. You have esc(args) and at the end esc again. That’s one of the difficulties of macro hygiene in Julia that this is not allowed.
The problem in this case is that you are escaping your variable twice in the returned expression.
It is very convenient to use the @macroexpand macro to verify what is the output of your macros to spot errors if something doesn’t work as intended.
In your case, using @macroexpand you would see:
julia> @macroexpand @my_test(s)
quote
#= REPL[8]:6 =#
s = add_variable($(Expr(:escape, :s)))
end
which shows that there is something weird on the arguments of add_variable.
If you remove your first line in the macro definition, everything should work as expected
julia> macro my_test(args)
var_call = :(add_variable($args))
return esc( quote $args = $var_call end)
end
julia> @macroexpand @my_test(s)
quote
#= REPL[10]:5 =#
s = add_variable(s)
end
A final question, as I was starting to use macroexpand to check my code, how did you tell (like the code you showed above) that there should be error from something like $(Expr(:escape, :s)) as the function argument? From my understanding, macroexpand is just showing the expression to be evaluated from macro return.
Except for the begin end that becomes quote end the output of @macroexpand should look like the code you’d write manually if you didn’t have the macro (for the most part, there are also linenumbernodes that you can ignore).
So whenever you see an Expr in the output of the @macroexpand, unless you are building macros to create expressions it means that something went wrong.
The $(Expr(:escape, _something_)) is quite a common pattern when starting to make macros as hygiene and escaping is not so straightforward so you are bound to find them a lot in the various macro definition attempts (at least I did and still do when creating macros). But you soon learn that it’s not supposed to be in the output of @macroexpand, and what it’s causing that (too many esc on a variable).