开发者

Why is the @ sign needed in this macro definition?

开发者 https://www.devze.com 2023-02-25 17:11 出处:网络
In the following when macro: (defmacro when (condition &rest body) `(if ,condition (progn ,@body)))

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))
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号