macroslispcommon-lispquasiquotes

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" @ sign?


Solution

  • When inserting computed values in quasiquoted section there are two operators:

    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]