Macros
Rules of macro club: (1) Don't write macros. (2) Unless you must: for new control flow, binding forms, or eliminating real boilerplate. Macros aren't values: you can't map or apply a macro.
Common pitfalls: double evaluation of arguments (bind them once), symbol capture (use name#), and forgetting that macros run at compile time.
;; A macro transforms code at compile time
(defmacro unless [test & body]
`(if ~test nil (do ~@body)))
(unless false (println "runs"))
(macroexpand-1 '(unless false (println "runs")))
;; => (if false nil (do (println "runs")))
;; Syntax-quote auto-qualifies symbols and supports auto-gensyms with #
(defmacro with-timing [& body]
`(let [start# (System/nanoTime)
result# (do ~@body)]
(println "Elapsed:" (/ (- (System/nanoTime) start#) 1e6) "ms")
result#))
(with-timing (Thread/sleep 50) :done)
;; Manual gensym
(defmacro my-or2 [a b]
(let [v (gensym "v")]
`(let [~v ~a] (if ~v ~v ~b))))
;; A def-style macro
(defmacro defhandler [name & body]
`(defn ~name [request#]
(try ~@body
(catch Exception e# {:status 500 :body (ex-message e#)}))))
;; Using &form and &env (advanced)
(defmacro show-locals [] `(quote ~(keys &env)))
(let [a 1 b 2] (show-locals)) ; (a b)
;; Tools
(macroexpand '(-> x f g)) ; full expansion of the top form
(clojure.walk/macroexpand-all '(when x (-> y f)))