在Lisp中(不确定Scala),宏主要用于以下三个目的:
- 定义:创建某些内容并直接注册到适当的位置。例如:
defun
, defgeneric
, defclass
(标准库),deftable
(postmodern)。
Unwind-protect
包装器:暂时修改状态,并确保在任务完成后将其修改回来。这样重复编写很麻烦,因此我们创建了一个简写。例如:with-open-file
(标准库),with-transaction
(许多数据库库)。
- 生成其他语言:例如CL-WHO(HTML),Parenscript(JavaScript)。通过在Lisp形式中生成其他语言的代码,即使它们本身不支持该功能,我们也可以为这些其他语言使用宏。
具体例子:Java 7引入了一种简写方式,以确保在try
块中关闭Closable
:
try (SomeClosable foo = openFoo()) {
foo.doSomething();
}
在Java 6中,只能大致地这样表达:
SomeClosable foo;
try {
foo = openFoo();
foo.doSomething();
} finally {
if (foo != null && foo.isOpen()) {
foo.close();
}
}
Java开发者需要等待语言设计师实现此功能。而Lisp开发者则使用一个小的宏:
(defmacro with-open-foo ((var &rest options) &body body)
`(let ((,var (open-foo ,@options)))
(unwind-protect
(progn ,@body)
(when ,var (close ,var)))))
这样他就可以写作了
(with-open-foo (f :bar baz)
(do-some-foo f)
(and-something-else))
替代
(let ((f (open-foo :bar baz)))
(unwind-protect
(progn
(do-some-foo f)
(and-something-else))
(when f (close f))))