Почему этот макрос Джулии _не_ требует `esc`?

Я нашел пример макроса unless в Джулии здесь, написанный как следует:

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

Однако, когда я пытаюсь использовать его, он терпит неудачу (видимо, есть проблема с гигиеной, но я не могу точно понять). Вот тест, который я использовал:

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

Теперь я могу заставить это работать, экранируя только ветку, а не тест:

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

Мой вопрос: почему достаточно только избежать ветки, но не теста? Теперь я попробовал макрорасширение. В первом случае без esc я получаю это:

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

Теперь, несмотря на то, что ни один параметр макроса не был экранирован, ТОЛЬКО y был сгенерирован! Кто-нибудь может объяснить, почему это произошло? (Я знаю, что вторая версия работает, потому что, когда я сбегаю из ветки, y не получает gensymed и макрос расширяется до y = 3, как и ожидалось. Но я совершенно не понимаю, почему x не был gensymed, хотя там было бесполезно esc.)


person Ray Toal    schedule 20.10.2015    source источник


Ответы (1)


Обратитесь к документу Джулии:

Переменные в результате макроса классифицируются как локальные или глобальные. Переменная считается локальной, если она назначена (и не объявлена ​​глобальной), объявлена ​​локальной или используется в качестве имени аргумента функции. В противном случае он считается глобальным....

Таким образом, в этом случае часть test ничего не присваивает, поэтому ее переменные считаются глобальными, но в части branch была назначена y, поэтому она считается локальной, и присвоение ей нового значения не изменяет y в области модуля.

person Reza Afzalan    schedule 20.10.2015
comment
Хорошо, я вижу, поэтому x классифицируется как глобальный, а y — как локальный. Глобальные объекты проходят без генерирования. Местные жители получают gensymed. Экранирование либо предотвращает генсиммирование, но не требуется для глобальных переменных. Логично, но не совсем очевидно. :) - person Ray Toal; 20.10.2015