@@ -2267,25 +2267,132 @@ catch specific Errors.
22672267
22682268=== Don't Write a Macro If a Function Will Do [[dont-write-macro-if-fn-will-do]]
22692269
2270- Don't write a macro if a function will do.
2270+ Don't write a macro if a function will do. Macros cannot be passed to
2271+ higher-order functions like `map`, `filter`, or `comp`, and they make
2272+ code harder to reason about. Reserve macros for when you truly need
2273+ control over evaluation.
2274+
2275+ [source,clojure]
2276+ ----
2277+ ;; good - a plain function works fine here
2278+ (defn square [x]
2279+ (* x x))
2280+
2281+ ;; bad - a macro that does nothing a function can't do
2282+ (defmacro square [x]
2283+ `(* ~x ~x))
2284+ ----
22712285
22722286=== Write Macro Usage before Writing the Macro [[write-macro-usage-before-writing-the-macro]]
22732287
22742288Create an example of a macro usage first and the macro afterwards.
2289+ Writing the desired call site first helps you design a clean API and
2290+ avoids over-engineering the macro.
2291+
2292+ [source,clojure]
2293+ ----
2294+ ;; Step 1: Write down how you want the macro to be used
2295+ (with-retry {:retries 3 :delay 1000}
2296+ (fetch-data url))
2297+
2298+ ;; Step 2: Then implement the macro to support that usage
2299+ (defmacro with-retry [{:keys [retries delay]} & body]
2300+ ...)
2301+ ----
22752302
22762303=== Break Complicated Macros [[break-complicated-macros]]
22772304
22782305Break complicated macros into smaller functions whenever possible.
2306+ Helper functions can be tested independently and called from other
2307+ code, while macro internals cannot.
2308+
2309+ [source,clojure]
2310+ ----
2311+ ;; good - helper functions handle parts of the code generation
2312+ (defn- emit-constructor [name fields]
2313+ `(defn ~(symbol (str "make-" name)) [~@fields]
2314+ (new ~name ~@fields)))
2315+
2316+ (defn- emit-predicate [name]
2317+ `(defn ~(symbol (str name "?")) [x#]
2318+ (instance? ~name x#)))
2319+
2320+ (defmacro defentity [name & fields]
2321+ `(do
2322+ (defrecord ~name [~@fields])
2323+ ~(emit-constructor name fields)
2324+ ~(emit-predicate name)))
2325+
2326+ ;; bad - everything inlined in one large macro
2327+ (defmacro defentity [name & fields]
2328+ `(do
2329+ (defrecord ~name [~@fields])
2330+ (defn ~(symbol (str "make-" name)) [~@fields]
2331+ (new ~name ~@fields))
2332+ (defn ~(symbol (str name "?")) [x#]
2333+ (instance? ~name x#))))
2334+ ----
22792335
22802336=== Macros as Syntactic Sugar [[macros-as-syntactic-sugar]]
22812337
22822338A macro should usually just provide syntactic sugar and the core of
22832339the macro should be a plain function. Doing so will improve
22842340composability.
22852341
2342+ [source,clojure]
2343+ ----
2344+ ;; good - the macro is thin sugar over a function
2345+ (defn perform-transaction [db-spec func]
2346+ (let [conn (get-connection db-spec)]
2347+ (try
2348+ (.setAutoCommit conn false)
2349+ (let [result (func conn)]
2350+ (.commit conn)
2351+ result)
2352+ (catch Exception e
2353+ (.rollback conn)
2354+ (throw e))
2355+ (finally
2356+ (.close conn)))))
2357+
2358+ (defmacro with-transaction [binding & body]
2359+ `(perform-transaction ~(second binding)
2360+ (fn [~(first binding)] ~@body)))
2361+
2362+ ;; bad - all logic lives in the macro
2363+ (defmacro with-transaction [binding & body]
2364+ `(let [conn# (get-connection ~(second binding))]
2365+ (try
2366+ (.setAutoCommit conn# false)
2367+ (let [~(first binding) conn#
2368+ result# (do ~@body)]
2369+ (.commit conn#)
2370+ result#)
2371+ (catch Exception e#
2372+ (.rollback conn#)
2373+ (throw e#))
2374+ (finally
2375+ (.close conn#)))))
2376+ ----
2377+
22862378=== Syntax Quoted Forms [[syntax-quoted-forms]]
22872379
22882380Prefer syntax-quoted forms over building lists manually.
2381+ Syntax-quote (backtick) auto-qualifies symbols, preventing accidental
2382+ capture, and is far easier to read than nested `list`/`cons` calls.
2383+
2384+ [source,clojure]
2385+ ----
2386+ ;; good - syntax-quote makes the output structure obvious
2387+ (defmacro when-not [test & body]
2388+ `(when (not ~test)
2389+ ~@body))
2390+
2391+ ;; bad - manual list construction is hard to follow
2392+ (defmacro when-not [test & body]
2393+ (list 'when (list 'not test)
2394+ (cons 'do body)))
2395+ ----
22892396
22902397== Common Metadata
22912398
0 commit comments