Hygienic vs Unhygienic Macros
defmacro (CL, Clojure, Elisp) Simple, full power (intentional capture: anaphoric macros like aif with it) Must manage capture manually syntax-rules (Scheme) Hygienic, declarative patterns Limited (no arbitrary computation)
syntax-case / syntax-parse (R6RS, Racket) Hygienic and fully programmable, great error messages
;; Unhygienic: variable capture bug (Common Lisp)
(defmacro bad-swap (a b)
`(let ((tmp ,a)) (setf ,a ,b) (setf ,b tmp)))
(let ((tmp 1) (other 2))
(bad-swap tmp other) ; breaks: the macro's tmp shadows the user's tmp
(list tmp other)) ; (1 2) instead of (2 1)
;; Fixed with gensym
(defmacro good-swap (a b)
(let ((tmp (gensym "TMP")))
`(let ((,tmp ,a)) (setf ,a ,b) (setf ,b ,tmp))))
;; Idiomatic CL: (rotatef a b);; Hygienic by default (Scheme): this just works
(define-syntax swap!
(syntax-rules ()
((_ a b) (let ((tmp a)) (set! a b) (set! b tmp)))))
(let ((tmp 1) (other 2))
(swap! tmp other)
(list tmp other)) ; (2 1);; Intentional capture: anaphoric if (Common Lisp, from "On Lisp")
(defmacro aif (test then &optional else)
`(let ((it ,test)) (if it ,then ,else)))
(aif (find 3 '(1 2 3)) (format t "found ~a~%" it))