There are three main purposes that macros are used for in Lisp (don't know about Scala):
- Defining: something is created and directly registered at the appropriate place. Examples:
defun
, defgeneric
, defclass
(from the standard), deftable
(from postmodern).
Unwind-protect
wrappers: a state is temporarily modified and ensured to be modified back after completion of a task. This can be cumbersome to write repeatedly, so we create a shorthand. Example: with-open-file
(standard), with-transaction
(many database libraries).
- Generation of other languages: for example, CL-WHO (HTML), Parenscript (JavaScript). By generating the code of other languages in Lisp forms, we can use macros for those other languages even if they do not have support for that themselves.
Concrete example: Java 7 introduced a shorthand for ensuring the closing of Closable
s in try
-blocks:
try (SomeClosable foo = openFoo()) {
foo.doSomething();
}
which can in Java 6 only be expressed roughly like this:
SomeClosable foo;
try {
foo = openFoo();
foo.doSomething();
} finally {
if (foo != null && foo.isOpen()) {
foo.close();
}
}
Java developers had to wait for the language designers to implement this feature. A Lisp developer uses a little macro:
(defmacro with-open-foo ((var &rest options) &body body)
`(let ((,var (open-foo ,@options)))
(unwind-protect
(progn ,@body)
(when ,var (close ,var)))))
so that he can write
(with-open-foo (f :bar baz)
(do-some-foo f)
(and-something-else))
instead of
(let ((f (open-foo :bar baz)))
(unwind-protect
(progn
(do-some-foo f)
(and-something-else))
(when f (close f))))