Search code examples
macrosjuliahygiene

Macro call vs macro definition environment in Julia


I am trying to make sense out of a statement in the Julia's Metaprogramming documentation on macro hygiene. The documentation claims that

Julia’s macro expander solves these problems in the following way. First, variables within a macro result are classified as either local or global. A variable is considered local if it is assigned to (and not declared global), declared local, or used as a function argument name. Otherwise, it is considered global. Local variables are then renamed to be unique (using the gensym() function, which generates new symbols), and global variables are resolved within the macro definition environment. Therefore both of the above concerns are handled; the macro’s locals will not conflict with any user variables, and time and println will refer to the standard library definitions.

I wrote a small program to see whether global variables were indeed resolved in the macro definition environment. I wrote the following:

f(x) = x + 100 

macro g()         # According to Julia docs, ...
  :(f(x) + 5)     # ... f is global, x is local, right?
end               # if so, f should refer to the f above?

(function main()
  local x = 3
  f(x) = x - 100  # f in the call environment subtracts 100
  println(@g())   # So why does this do -92?
end)()

If I am to understand the Julia docs correctly, part of macro hygiene is to make sure whichever functions are called in the macro's returned expression don't get hijacked by functions of the same name inside the caller's environment. But this is exactly what happens here, the function f that was used was the one that was defined locally.

I would have thought I would have to use esc in order to use the f in scope at the point of call. But this is not so, why?

And in addition, I noticed that the the variable x inside the macro result is considered local, so a new gensymed variable name should have been generated for it, so as not to clash with the x in the macro call environment. But this did not happen either!

How am I to read the documentation to make any sense out the reason that esc need not be used here?

EDIT (CLARIFICATION)

  1. When I claim f is global and x is local, according to the docs, I do so because I see that x is used as a function argument. I understand x is not being written to nor declared local, and it certainly looks global, but those docs claim function arguments should be global too!

  2. I know the usual part of hygiene where the gensymming of locals ensures that variables of the same name in the macro caller's context are not inadvertently plastered. However, the docs claim that for functions, the ones that are seen in the macro definition's context are protected from the caller using their own. This is the part that makes no sense to me, because my experiment shows otherwise.


Solution

  • This is a bug. It's an old issue of Julia 0.5 and earlier, and has been fixed in Julia 0.6. See https://github.com/JuliaLang/julia/issues/4873 for more information.