I don't write a lot of macros. For the small apps and improvisational code I use Clojure for, there isn't a lot of repetition to streamline by adding syntax. Every now and then I do tinker a little, so here are a few small macros I've collected over time.
The first is cond-let
, which nests calls to if-let
by calling itself recursively:
(defmacro cond-let [nm & clauses]
(when (seq clauses)
`(if-let [~nm ~(first clauses)]
~(second clauses)
(cond-let ~nm ~@(drop 2 clauses)))))
Unlike if-let
, cond-let
doesn't use a vector as a binding form but instead assigns the result of each clause to the name given as the macro's first argument:
(let [word "beta"]
(cond-let result
(some #{\x \y \z} word)
, (str "xyz: " result)
(some #{\a \b \c} word)
, (str "abc: " result)))
"abc: b"
If you want a more sophisticated version of cond
, check out Mark Engelberg's better-cond.
Here's a macro that evaluates the values of a hash-map in parallel:
(defmacro parallel-hash-map [m]
(let [ks (vec (keys m))
vs (vals m)]
`(zipmap ~ks (pvalues ~@vs))))
Note the call to vec
. The list of map keys has to be converted to a vector, otherwise it expands into a function invocation.
I've used this macro to gather the results of several HTTP calls without having each call run serially:
(time
(parallel-hash-map
{:a (do (Thread/sleep 100) 1)
:b (do (Thread/sleep 100) 2)}))
"Elapsed time: 105.225917 msecs"
{:a 1, :b 2}
ECMAScript 6 allows words in object literals to serve as both a key and a value, enabling expressions such as {a, b}
to be a shorthand for {a: a, b: b}
. We can macro something similar in Clojure:
(defmacro kv-map [& symbols]
`(zipmap
~(mapv keyword symbols)
~(vec symbols)))
(let [a 1
b 2]
(kv-map a b))
{:a 1, :b 2}
ES6 also allows mixing the above syntax with traditional object field/value pairs, e.g. {a: 1, b}
, but that's tricky to replicate in a Clojure macro without introducing additional delimiters and some level of parsing.
If you have additional macros, feel free to email me.