diff --git a/doc/misc/cl.texi b/doc/misc/cl.texi index 57e2f3a6c3b..de9f0565d03 100644 --- a/doc/misc/cl.texi +++ b/doc/misc/cl.texi @@ -2684,10 +2684,10 @@ Macro-Writing Macros @end defmac @defmac cl-once-only* (variable forms) body -This macro is a version of @code{cl-once-only} which takes a list of -forms. This macro is primarily meant to be used where the number of -forms is unknown and thus @code{cl-once-only} cannot work, such as those -obtained by a @code{&body} argument. +This macro is a version of @code{cl-once-only} which takes an +arbitrarily long list of forms. This macro is primarily meant to be +used where the number of forms is unknown and thus @code{cl-once-only} +cannot work, such as those obtained by a @code{&body} argument. Each element of @var{variable} may be used to refer to the result of evaluating the corresponding form in @var{forms} within @var{body}. @@ -2696,38 +2696,58 @@ Macro-Writing Macros such that each form is evaluated in order and its result is bound to the corresponding symbol. -Like @code{cl-once-only}, the first argument can be a symbol -@var{variable}, which is equivalent to writing @code{(variable -variable)}. +Like @code{cl-once-only}, the first argument can be a symbol @var{variable}, which +is equivalent to writing @code{(variable variable)}. Consider the following macro: @example -(defmacro my-list (head &rest args) - (cl-once-only ((args `(list ,@@args)) - `(list (apply #',head ,args) - ,args - (nth 1 ,args)))) +(defmacro my-list (vals &rest forms) + (let ((val-results (mapcar (lambda (_) (gensym)) vals))) + `(let* ,(cl-mapcar #'list val-results vals) + (list ,(cl-first val-results) + ,(cl-second val-results) + ,@@val-results + (progn ,@@forms))))) @end example -This macro is such that it will evaluate @var{args} only once, however -that @var{args} was a list is lost once we are in @code{cl-once-only}. -Furthermore, to access any specific element of @var{args} we must obtain -the element during evaluation via @code{(nth N ,args)}. +In a call like @code{(my-list ((pop foo) (cl-incf bar) ...) ...)} the +@code{pop} and @code{cl-incf} will be evaluated exactly once, ensuring +their side effects are not applied twice. This code is however very +complex, in the same way code not using @code{cl-once-only} is. -Consider the alternative using @code{cl-once-only*}: +Using @code{cl-once-only} is not possible directly due to it expecting +individual forms which can be evaluated. This can be worked around by +assigning to a variable @code{`(list ,@@vars)} which @emph{can} be +evaluated: @example -(defmacro my-list (head &rest args) - (cl-once-only* args - `(list (,head ,@@args) - (list ,@@args) - ,(nth 1 args)))) +(defmacro my-list (vals &rest forms) + (cl-once-only ((vals `(list ,@@vals))) + `(list (cl-first ,vals) + (cl-second ,vals) + ,vals ; Does not splice + (progn ,@@forms)))) +@end example + +There are two problems which both result from the fact that @code{vals} +is not a list inside the body of @code{cl-once-only}: 1. @code{vals} +cannot be spliced in the way it can in the previous example and +2. accessing individual elements of @code{vals} can only be done by +accessing the resulting list @emph{during evaluation}. Compare this to +the example using @code{cl-once-only*}: + +@example +(defmacro my-list (vals &rest forms) + (cl-once-only* vals + `(list ,(cl-first vals) + ,(cl-second vals) + ,@@vals + (progn ,@@forms)))) @end example -which preserves the fact that @var{args} is a list and allows immediate -access to individual arguments by simply choosing the corresponding -element in @var{args}. +which preserves the fact the @var{vals} is a list and removes +boiler-plate code for generating and assigning temporary variables. @end defmac @node Macros