Skip to content

Commit 93de493

Browse files
authored
Merge pull request #267 from bbatsov/add-macro-section-examples
Add examples and rationale to the Macros section
2 parents 0a72ead + fa39369 commit 93de493

File tree

1 file changed

+108
-1
lines changed

1 file changed

+108
-1
lines changed

README.adoc

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

22742288
Create 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

22782305
Break 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

22822338
A macro should usually just provide syntactic sugar and the core of
22832339
the macro should be a plain function. Doing so will improve
22842340
composability.
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

22882380
Prefer 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

Comments
 (0)