Оценивание аргументов макроса при развёртывании
Другая проблема может возникнуть, если определение макроса само оценивает какие-либо выражения аргументов макроса, такие как вызов eval (см. Eval). Необходимо учитывать, что развёртывание макроса может происходить задолго до выполнения кода, когда контекст вызывающего (где будет оценено развёртывание макроса) ещё недоступен.
Также, если определение вашего макроса не использует lexical-binding, его формальные аргументы могут скрывать переменные пользователя с тем же именем. Внутри тела макроса привязка аргумента макроса является самой локальной привязкой такой переменной, поэтому любые ссылки внутри оцениваемого выражения относятся к ней. Вот пример:
(defmacro foo (a) (list 'setq (eval a) t))
(setq x 'b)
(foo x) → (setq b t)
⇒ t ; and b has been set.
;; but
(setq a 'c)
(foo a) → (setq a t)
⇒ t ; but this set a, not c.
Это имеет значение, называется ли переменная пользователя a или x, потому что a конфликтует с переменной аргумента макроса a.
Кроме того, развёртывание (foo x) выше вернёт что-то другое или сообщит об ошибке при компиляции кода, так как в этом случае (foo x) развёртывается во время компиляции, в то время как выполнение (setq x 'b) произойдёт только позже, при выполнении кода.
Чтобы избежать этих проблем, не оценивайте выражение аргумента во время вычисления развёртывания макроса. Вместо этого подставьте выражение в развёртывание макроса, чтобы его значение вычислялось как часть выполнения развёртывания. Именно так работают другие примеры в этой главе.
Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Eval-During-Expansion.html