Previous: Multiple Values, Up: Control Structure [Contents][Index]
This package includes two classic Common Lisp macro-writing macros to help render complex macrology easier to read.
This macro expands to code that executes body with each of the
variables in names bound to a fresh uninterned symbol, or
gensym, in Common Lisp parlance. For macros requiring more than
one gensym, use of cl-with-gensyms
shortens the code and
renders one’s intentions clearer. Compare:
(defmacro my-macro (foo) (let ((bar (gensym "bar")) (baz (gensym "baz")) (quux (gensym "quux"))) `(let ((,bar (+ …))) …))) (defmacro my-macro (foo) (cl-with-gensyms (bar baz quux) `(let ((,bar (+ …))) …)))
This macro is primarily to help the macro programmer ensure that forms supplied by the user of the macro are evaluated just once by its expansion even though the result of evaluating the form is to occur more than once. Less often, this macro is used to ensure that forms supplied by the macro programmer are evaluated just once.
Each variable may be used to refer to the result of evaluating
form in body. cl-once-only
binds each
variable to a fresh uninterned symbol during the evaluation of
body. Then, cl-once-only
wraps the final expansion in
code to evaluate each form and bind the result to the
corresponding uninterned symbol. Thus, when the macro writer
substitutes the value for variable into the expansion they are
effectively referring to the result of evaluating form, rather
than form itself. Another way to put this is that each
variable is bound to an expression for the (singular) result of
evaluating form.
The most common case is where variable is one of the arguments
to the macro being written, so (variable variable)
may be
abbreviated to just variable
.
For example, consider this macro:
(defmacro my-list (x y &rest forms) (let ((x-result (gensym)) (y-result (gensym))) `(let ((,x-result ,x) (,y-result ,y)) (list ,x-result ,y-result ,x-result ,y-result (progn ,@forms))))
In a call like (my-list (pop foo) …)
the intermediate
binding to x-result
ensures that the pop
is not done
twice. But as a result the code is rather complex: the reader must
keep track of how x-result
really just means the first
parameter of the call to the macro, and the required use of multiple
gensyms to avoid variable capture by (progn ,@forms)
obscures
things further. cl-once-only
takes care of these details:
(defmacro my-list (x y &rest forms) (cl-once-only (x y) `(list ,x ,y ,x ,y (progn ,@forms))))
Previous: Multiple Values, Up: Control Structure [Contents][Index]