17.4 Вычисление во время компиляции

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

Макрос: eval-and-compile body…

Эта форма указывает, что body должен вычисляться как при компиляции содержащего его кода, так и при его выполнении (скомпилированного или нет).

Похожего результата можно добиться, поместив body в отдельный файл и обратившись к этому файлу с помощью require. Этот способ предпочтительнее, когда body велик. Фактически require автоматически действует как eval-and-compile — пакет загружается как при компиляции, так и при выполнении.

autoload также фактически является eval-and-compile. Он распознаётся при компиляции, поэтому использование такой функции не вызывает предупреждений «не известно, что определена».

Большинство применений eval-and-compile довольно сложны.

Если у макроса есть вспомогательная функция для построения его результата, и этот макрос используется как локально, так и за пределами пакета, то eval-and-compile следует использовать, чтобы вспомогательная функция была доступна как при компиляции, так и впоследствии при выполнении.

Если функции определяются программно (скажем, с помощью fset), то eval-and-compile можно использовать, чтобы это выполнялось как во время компиляции, так и во время выполнения, чтобы вызовы этих функций проверялись (а предупреждения «не известно, что определена» подавлялись).

Макрос: eval-when-compile body…

Эта форма указывает, что body должен вычисляться во время компиляции, но не при загрузке скомпилированной программы. Результат вычисления компилятором становится константой, которая появляется в скомпилированной программе. Если вы загружаете исходный файл, а не компилируете его, body вычисляется обычным образом.

Если у вас есть константа, для получения которой требуются некоторые вычисления, eval-when-compile может выполнить их во время компиляции. Например,

(defvar gauss-schoolboy-problem
  (eval-when-compile (apply #'+ (number-sequence 1 100))))

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

(eval-when-compile
  (require 'my-macro-package))

То же самое относится к макросам и функциям defsubst, определённым локально и используемым только внутри файла. Они нужны для компиляции файла, но в большинстве случаев не требуются для выполнения скомпилированного файла. Например,

(eval-when-compile
  (unless (fboundp 'some-new-thing)
    (defmacro some-new-thing ()
      (compatibility code))))

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

Замечание для Common Lisp: На верхнем уровне eval-when-compile аналогичен идиоме Common Lisp (eval-when (compile eval) …). В других местах ридер-макрос Common Lisp ‘#.’ (но не при интерпретации) ближе по смыслу к тому, что делает eval-when-compile.