Eval - когда используется?

После прочтения большого количества документации относительно оператора Lisp eval-when я все еще не могу понять его использование, я знаю, что с этим оператором я могу контролировать время оценки своих выражений, но я не могу понять какой-либо пример, где это может быть применимо?

С наилучшими пожеланиями, utxeee.

Ответ 1

Компиляция файла Lisp

Возьмем, например, компиляцию файла Lisp. Компилятор Lisp обрабатывает формы верхнего уровня. Это могут быть произвольные формы Lisp, DEFUN, DEFMACROS, DEFCLASS, вызовы функций,...

Вся история, как работает компилятор файлов, слишком сложна для объяснения здесь, но несколько вещей:

  • компилятор файла генерирует код для формы (DEFUN foo () ). Но он не выполняет форму defun. Таким образом, во время компиляции известно, что существует функция FOO, но код "FOO" недоступен во время компиляции. Компилятор генерирует код для скомпилированного файла, но не сохраняет его в памяти. Вы не можете вызвать такую ​​функцию во время компиляции.

  • для макросов это несколько отличается: (DEFMACRO BAZ ...). Компилятор файла не только скомпилирует макрос и заметит, что он есть, но также сделает макрос доступным во время компиляции. Он загружается в среду компилятора.

Итак, представьте себе последовательность форм в файле:

(defmacro baz ...)

(defun foo () (baz ...))

Это работает, потому что компилятор файла знает макрос BAZ, и когда он компилирует код для FOO, тогда он может развернуть форму макроса.

Теперь рассмотрим следующий пример:

(defun bar (form) ...)

(defmacro baz (form) (bar form))

(defun foo () (baz ...))

Выше не будет работать. Теперь макрос BAZ использует функцию BAR, вызывая его. Когда компилятор пытается скомпилировать функцию FOO, он не может развернуть макрос BAZ, потому что BAR не может быть вызван, потому что код BAR не загружается в среду компиляции.

Есть два решения:

  • скомпилировать и загрузку BAR раньше, используя отдельный файл.
  • Используйте EVAL-WHEN

Пример для EVAL-WHEN:

 (eval-when (:compile-toplevel :execute :load-toplevel)
   (defun bar (form) ...)
 )

 (defmacro baz (form) (bar form))

 (defun foo () (baz ...))

Теперь EVAL-WHEN инструктирует компилятор файла фактически запустить форму DEFUN во время компиляции. Эффект этого: компилятор файла теперь знает определение BAR во время компиляции. Таким образом, он доступен позже, когда компилятор файла должен вызвать BAR во время макрорасширения использования BAZ.

Можно использовать только :compile-toplevel, когда функция не понадобится после компиляции файла. Если он используется позже, нам нужно убедиться, что он загружен.

Итак, EVAL-WHEN позволяет указать, должен ли выполняться конкретный фрагмент кода

  • при компиляции файла
  • при загрузке файла
  • во время выполнения

EVAL-WHEN не используется часто в коде пользователя. Если вы его используете, тогда вы должны спросить себя, действительно ли вам это нужно.