Why is the @ sign needed in this macro definition?
In the following when
macro:
(defmacro when (condition &rest body)
`(if ,condition (progn ,@body)))
Why is there an "at" @
开发者_C百科 sign?
When inserting computed values in quasiquoted section there are two operators:
- The "comma" operator
,
- The "comma-splice" operator
,@
Comma ,
inserts the value of following expression in the quasi-quoted sexpr, comma-splice instead requires the expression following is a list and can be used only inside a quasi-quoted list: the effect is inserting all elements of the expression in the quasi-quoted list in the position where the operator appears.
It's very easy to see the difference by making a little experiment
> (let ((x '(1 2 3 4))) `(this is an example ,x of expansion))
(THIS IS AN EXAMPLE (1 2 3 4) OF EXPANSION)
> (let ((x '(1 2 3 4))) `(this is an example ,@x of expansion))
(THIS IS AN EXAMPLE 1 2 3 4 OF EXPANSION)
As you can see the use of ,@
will place the elements of the list directly inside in the expansion. Without you get instead the list placed in the expansion.
Using ,@
with an expression that doesn't result in a list will be an error when the substitution is performed:
* (defun f (x) `(here ,@x we go))
F
* (f '(1 2 3))
(HERE 1 2 3 WE GO)
* (f '99)
debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {10009F80D3}>:
The value
99
is not of type
LIST
when binding SB-IMPL::X
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(SB-IMPL::APPEND2 99 (WE GO)) [external]
0]
Using ,@
not inside a list is instead an error when the quasi-quoted section is analyzed:
* (defun g (x) `,@x)
debugger invoked on a SB-INT:SIMPLE-READER-ERROR in thread
#<THREAD "main thread" RUNNING {10009F80D3}>:
`,@X is not a well-formed backquote expression
Stream: #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDIN* {10000279E3}>
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(SB-IMPL::BACKQUOTE-CHARMACRO #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDIN* {10000279E3}> #<unused argument>)
0]
The @
can also be thought of deconstructing the list and appending it to the list appears in as described in Practical Common Lisp.
`(a ,@(list 1 2) c)
is the equivalent of:
(append (list 'a) (list 1 2) (list 'c))
which produces:
(a 1 2 c)
That macro definition is equivalent to
(defmacro when (condition &rest body)
(list 'if condition (cons 'progn body)))
but without the @
it would be equivalent to
(defmacro when (condition &rest body)
(list 'if condition (list 'progn body)))
Since body
is a list, that would cause it to be evaluated as if a parenthesized function call, e.g. (when t 1 2 3)
would get expanded as
(if t (progn (1 2 3)))
instead of the correct
(if t (progn 1 2 3))
精彩评论