这些特性允许你编写在程序编译阶段执行的代码。
该形式标记 body 部分,使其在编译包含此代码的文件时执行, 且在运行该代码(无论是否已编译)时也会执行。
你也可以将 body 放入独立文件,再通过 require 引用该文件,
达到类似效果。当 body 内容较多时,推荐使用这种方式。
实际上 require 本身就等效于自动应用了 eval-and-compile,
对应的包会在编译和执行阶段均被加载。
autoload 同样等效于隐式的 eval-and-compile。
编译器在编译时会识别 autoload 定义,
因此调用这类函数不会产生「未定义」警告。
eval-and-compile 的大多数用法都具有较强的专业性:
若某个宏依赖辅助函数构建结果,且该宏既在包内部使用、也对外暴露,
则需通过 eval-and-compile 确保辅助函数在编译和运行阶段均可用;
若函数是通过编程方式定义的(例如使用 fset),
eval-and-compile 可让定义逻辑在编译和运行阶段都执行,
从而使编译器能检查这些函数的调用(并抑制 “未定义(not known to be defined)” 警告)。
该形式标记 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 的行为。