14.5.3 Локальные переменные в расширениях макросов

В предыдущем разделе определение for было изменено следующим образом, чтобы расширение вычисляло аргументы макроса нужное количество раз:

(defmacro for (var from init to final do &rest body)
  "Выполнить простой цикл for: (for i from 1 to 10 do (print i))."
  `(let ((,var ,init)
         (max ,final))
     (while (<= ,var max)
       ,@body
       (inc ,var))))

Новое определение for имеет новую проблему: оно вводит локальную переменную с именем max, которую пользователь не ожидает. Это вызывает проблемы в таких примерах, как следующий:

(let ((max 0))
  (for x from 0 to 10 do
    (let ((this (frob x)))
      (if (< max this)
          (setq max this)))))

Ссылки на max внутри тела for, которые должны ссылаться на привязку max пользователя, на самом деле обращаются к привязке, созданной for.

Способ исправления этого — использовать неинтернируемый символ вместо max (см. Создание и интернирование символов). Неинтернируемый символ можно привязать и на который можно ссылаться, как и на любой другой символ, но поскольку он создается for, мы знаем, что он не может уже присутствовать в программе пользователя. Поскольку он не интернирован, нет способа, которым пользователь может добавить его в программу позже. Он никогда не появится нигде, кроме как там, где его поместил for. Вот определение for, которое работает таким образом:

(defmacro for (var from init to final do &rest body)
  "Выполнить простой цикл for: (for i from 1 to 10 do (print i))."
  (let ((tempvar (make-symbol "max")))
    `(let ((,var ,init)
           (,tempvar ,final))
       (while (<= ,var ,tempvar)
         ,@body
         (inc ,var)))))

Это создает неинтернируемый символ с именем max и помещает его в расширение вместо обычного интернированного символа max, который появляется в выражениях обычно.