14.5.4 Вычисление аргументов макроса во время расширения

Еще одна проблема может возникнуть, если само определение макроса вычисляет какие-либо выражения аргументов макроса, например, вызывая eval (см. Eval). Необходимо учитывать, что расширение макроса может произойти задолго до выполнения кода, когда контекст вызывающей стороны (где будет вычислено расширение макроса) еще недоступен.

Кроме того, если ваше определение макроса не использует lexical-binding, его формальные аргументы могут скрывать переменные пользователя с тем же именем. Внутри тела макроса привязка аргумента макроса является самой локальной привязкой такой переменной, поэтому любые ссылки внутри вычисляемой формы ссылаются на нее. Вот пример:

(defmacro foo (a)
  (list 'setq (eval a) t))
(setq x 'b)
(foo x) → (setq b t)
     ⇒ t                  ; и b было установлено.
;; но
(setq a 'c)
(foo a) → (setq a t)
     ⇒ t                  ; но это устанавливает a, а не c.

Важно, имеет ли имя переменной пользователя a или x, потому что a конфликтует с переменной аргумента макроса a.

Кроме того, расширение (foo x) выше вернет что-то другое или вызовет ошибку при компиляции, поскольку в этом случае (foo x) расширяется во время компиляции, тогда как выполнение (setq x 'b) произойдет только позже, когда код будет выполнен.

Чтобы избежать этих проблем, не вычисляйте выражение аргумента во время вычисления расширения макроса. Вместо этого подставьте выражение в расширение макроса, чтобы его значение было вычислено как часть выполнения расширения. Так работают другие примеры в этой главе.