In the official documentary, a macro example is provided:
macro time(ex)
return quote
local t0 = time()
local val = $ex
local t1 = time()
println("elapsed time: ", t1-t0, " seconds")
val
end
end
After this macro, a problem is immediately brought up:
module MyModule
import Base.@time
time() = ... # compute something
@time time()
end
In the above snippet, ex
in the macro definition will be resolved to the standard time function, not the one defined in the snippet.
This behavior seems to be quite strange to me. Naively, I would think since ex
is obtained from macro calling environment, how come it is resolved in macro defining environment? How is macro arguments resolve exactly?
Please finish reading that page of doc.
I finished the page before I asked. I know that the way out is to use esc
, but I still don’t understand why ex
should be resolved in macro defining environment. If I do
module MyModule
import Base.@time
myfun() = ... # compute something
@time myfun()
end
then myfun
is not even defined in macro defining environment.
Right? What’s the problem? The base one should have the correct behavior and resolve to your function. The first example in the hygiene section is just demonstrating the problem and it is corrected a few lines later.
Does this mean macro arguments should always be escaped?
Not always but yes when you want them to be treated as normal code.
Anything that deconstruct the expression passed in. Or does not treat the input as normal variables. You may also want to resolve some symbols in the macro’s environment. The cartisian loop macros, @static
, @compat
, @pyimport
etc are all doing that to some degrees. In general, macros are way more powerful than simply wrapping some code in some enviroment. The macro can use all syntactic information in the function to do all what it want, which for any non-trivial transformation will not want the arguments to be escaped directly.
No macro arguments do not almost always need to be escaped. In many cases they’ll be escaped as pieces. Escaping pieces by default could be a better way but it’s not possible without huge breakage since it’s not at all under the runtime’s control currently, let alone the parser. Ref RFC: WIP: Make macro hygiene easier to use and less error-prone by vtjnash · Pull Request #10940 · JuliaLang/julia · GitHub
Thank you for your explanation. @yuyichao
It’s also possible to just escape everything and use gensym
to avoid name collisions.
And interpolate in any local variables and functions