4
votes

I found an example of an unless macro in Julia here written as follows:

macro unless(test, branch)
  quote
    if !$test
      $branch
    end
  end
end

However, when I try to use it, it fails (apparently there is a hygiene problem, but I can't figure it out exactly). Here is the test that I used:

x, y = 0, 1
@unless (x == 5) begin   # should execute
  y = 3
end
@unless (x == 0) begin   # should not execute
  y = 5
end
@assert y == 3           # FAILS! SAYS y is 0

Now I can make this work by escaping only the branch, not the test:

macro unless(test, branch)
  quote
    if !$test
      $(esc(branch))
    end
  end
end

My question is: why does it suffice to only escape the branch but not the test? Now I did try macroexpanding. In the first case, without the esc, I get this:

julia> macroexpand(:(@unless (x == 5) begin y = 3 end))
quote  # none, line 3:
    if !(x == 5) # none, line 4:
        begin  # none, line 1:
            #2#y = 3
       end
    end
end

Now even though neither macro parameter was escaped, ONLY the y was gensymed! Can anyone explain why this was the case? (I know that the second version works because when I escape the branch, The y doesn't get gensymed and the macro expands to y = 3 as expected. But I'm totally at a loss as to why the x was not gensymed even though there was no use of esc.)

1

1 Answers

5
votes

Refer to Julia doc:

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....

So in this case test part does not assign anything therefore it's variables considered global, but in branch part, y got assigned thus it is considered local and assigning new value to it do not change y in the module scope.