Lisp 宏对象是一个列表:其 CAR 部分为符号 macro,CDR 部分是一个函数。宏的展开过程,本质是将该函数(通过 apply 调用)作用于宏调用中 未求值 的参数列表。
我们可以像使用匿名函数一样使用匿名 Lisp 宏,但这种做法从未被实际采用—— 因为将匿名宏传递给 mapcar 这类函数式工具毫无意义。在实际应用中,所有 Lisp 宏都有名称,且几乎总是通过 defmacro 宏来定义。
defmacro 会将符号 name(无需加引号)定义为如下形式的宏:
(macro lambda args . body)
(注意:该列表的 CDR 部分是一个 lambda 表达式。)这个宏对象会被存储到 name 的函数单元(function cell)中。args 的含义与函数参数完全一致,且可使用 &rest 和 &optional 关键字(see 参数列表的特性)。name 和 args 均无需加引号。defmacro 的返回值未定义。
若存在 doc 参数,其值应为一个字符串,用于指定该宏的文档字符串(documentation string)。若存在 declare 参数,其值应为一个 declare 形式,用于指定宏的元数据(see declare 形式)。需注意,宏不支持交互式声明(interactive declarations),因为宏无法以交互方式调用。
宏常常需要结合常量和非常量部分来构造复杂的列表结构。为简化这一操作,可使用 ‘`’ 语法(see 反引号)。例如:
(defmacro t-becomes-nil (variable)
`(if (eq ,variable t)
(setq ,variable nil)))
(t-becomes-nil foo)
≡ (if (eq foo t) (setq foo nil))