Search code examples
macroslispelisp

Lisp - Utilisation of comma (`,`) in macro


I am practising writing macros in Elisp. The teacher suggested us to include the macro part of the program in "quasiquote".

I would like to write a macro "printCdrEach" that print each element of cdr of the list (Just for the sake of practicing)

(defmacro print-cdr-each (first &rest rest)
  `(while (not (null ,rest))
    (eval (car ,rest))
     (setq rest (cdr ,rest)))
  )

(print-cdr-each 1 2 3 4) ; Expected Output 2 3 4

I keep getting the error "Invalid function: 2". I am sure that the compiler thought 2 is a function. However, I am not sure how to fix this program.

Without changing the input format (printCdrEach x x x x) , what is the appropriate use of "," in my program in order for it to work?


Solution

  • You should try macroexpand-1:

    (macroexpand-1 '(print-cdr-each 1 2 3 4))
    ; ==> (while (not (null (2 3 4))) 
    ;       (eval (car (2 3 4))) 
    ;       (setq rest (cdr (2 3 4)))) 
    ; ==> t
    
    • Expression (2 3 4) is invalid. Try it in the REPL and you get eval: 1 is not a function name
    • You are using rest several times so if it was an expression it would be evaluated more than once.

    There is really no need for a macro:

    (defun for-each (operation &rest elements)
      (loop :for e :in elements
            :do (funcall operation e)))
    
    (for-each #'print 10 20)
    10
    20
    ; ==> nil
    

    Macros are syntax sugar. That means you should be able to say what (print-cdr-each 1 2 3 4) should expand to in code. Since you are doing it as a macro you may want to expand it to static:

    (defmacro for-each (operation &rest elements)
      `(progn ,@(loop :for e :in elements 
                      :collect `(,operation ,e))))
    
    (macroexpand-1 '(for-each print a b))
    ; ==> (progn (print a) (print b))
    ; ==> t
    
    (let ((a 10) (b 20)) (for-each print a b))
    10
    20
    ; ==> 20