common-lispccllisp-macros

Why is a macro being evaluated while compiling a function definition (Clozure Common Lisp)?


I have:

(defmacro assign (name value)
  (format t "assigning ~A to ~A~%" `,name `,value))

(defun opcode-call (&rest args)
  (mapcar (lambda (arg) 
             (if (stringp arg) 
                 (let ((var (gensym)))
                   (assign var arg)
                   var) 
                 arg)) 
          args))

When I compile opcode-call, REPL outputs:

assigning VAR to ARG
OPCODE-CALL

Why is assign being evaluated at compile time?


Solution

  • Macros are functions. They take code via their arguments and return new code. Macros can have side effects.

    Your code prints something as a side effect during macro expansion and returns NIL (the result of calling the FORMAT function).

    (defmacro assign (name value)
      (format t "assigning ~A to ~A~%" `,name `,value))
    

    Using it:

    CL-USER 11 > (multiple-value-list (macroexpand '(assign foo bar)))
    assigning FOO to BAR      ; prints as a side effect
    (NIL T)                   ; the macro expansion returns two values NIL and T
    

    It does not make sense to quote the arguments. The code is equivalent to this:

    (defmacro assign (name value)
      (format t "assigning ~A to ~A~%" name value))
    

    It still returns NIL as the expansion, which is probably not what you want.

    If you want the macro to expand a form into the call to format, then you need to return that call as a list. Here we use quasiquote to construct a list from a template, filling in two values: name and value.

    (defmacro assign (name value)
      `(format t "assigning ~A to ~A~%" ,name ,value))
    

    Maybe you want to quote the name:

    (defmacro assign (name value)
      `(format t "assigning ~A to ~A~%" ',name ,value))