syntax-rules

Hygiene has two guarantees: (1) identifiers introduced by the macro (like tmp) don't capture user variables; (2) free identifiers in the template (like let, if) refer to their bindings where the macro was defined, even if the user rebinds them locally.

;; (define-syntax name (syntax-rules (literals ...) (pattern template) ...))
(define-syntax my-unless
  (syntax-rules ()
    ((_ test body ...)                ; _ matches the macro keyword; `...` matches zero or more
     (if test #f (begin body ...)))))
(my-unless (> 1 2) (display "1 is not greater than 2"))

(define-syntax swap!
  (syntax-rules ()
    ((_ a b) (let ((tmp a)) (set! a b) (set! b tmp)))))
(define tmp 1) (define other 2)
(swap! tmp other)                     ; works: hygiene keeps the macro's tmp separate
(list tmp other)                      ; (2 1)

;; Multiple patterns, recursion
(define-syntax my-or
  (syntax-rules ()
    ((_) #f)
    ((_ e) e)
    ((_ e rest ...) (let ((t e)) (if t t (my-or rest ...))))))
(my-or #f #f 3)                       ; 3

(define-syntax my-let*
  (syntax-rules ()
    ((_ () body ...) (let () body ...))
    ((_ ((name val) rest ...) body ...)
     (let ((name val)) (my-let* (rest ...) body ...)))))
(my-let* ((x 1) (y (+ x 1))) (* x y)) ; 2

;; Literals: keywords that must appear verbatim
(define-syntax for
  (syntax-rules (in from to)
    ((_ x in lst body ...) (for-each (lambda (x) body ...) lst))
    ((_ x from start to end body ...)
 
…