Skip to content
Defining Macros
step 1/5

Reading — step 1 of 5

Learn

~1 min readIteration and Macros

Macros are functions that operate on code at compile time. Lisp's killer feature.

A macro takes unevaluated code (as lists/symbols) and returns code that gets compiled.

(defmacro when-positive (n &body body)
    `(if (> ,n 0)
         (progn ,@body)
         nil))

(when-positive 5
    (print "hi")
    (print "there"))
;; expands to:
;; (if (> 5 0)
;;     (progn (print "hi") (print "there"))
;;     nil)

Quasiquote / unquote syntax:

  • ` — quasiquote (template)
  • , — unquote (substitute the value here)
  • ,@ — splice (substitute and flatten)
  • &body — collect rest as a list

Macro expansion order:

  1. Reader parses source into lists
  2. Macros run, transforming lists into other lists
  3. Compiler compiles the result

This means you can write code that writes code — adding new control structures, abstractions, DSLs.

Example: a swap macro:

(defmacro swap (a b)
    (let ((tmp (gensym)))      ; gensym = unique symbol — avoids name capture
        `(let ((,tmp ,a))
            (setf ,a ,b)
            (setf ,b ,tmp))))

(let ((x 1) (y 2))
    (swap x y)
    (format t "~a ~a~%" x y))    ; 2 1

gensym generates a unique symbol — critical to avoid clashing with names in the user's code.

Macros are how Common Lisp libraries like iterate, loop, defclass, with-open-file are built. They're transparent — you can macroexpand to see what they produce.

macroexpand-1 / macroexpand — see what your macro generates:

(macroexpand-1 '(when-positive 5 (print x)))

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…