This article is the last part of our Clojure macros tutorial. The previous parts were:

  1. functions vs. macros
  2. how not to write macros
  3. syntax quote

Now we are going to show you how clojure ninjas write macros.

Ninja

The disp macro - by 2 anonymous ninjas

The disp macro receives expressions and returns a symbol with the expressions and their respective evaluations. (A symbol is much more pretty than a string.)


(ns my.best$macros)

(defn symbol-several
  "returns a symbol with the concatenation of the str values of the args"
  [& x]
  (symbol (apply str x)))

(defmacro disp [& forms]
  (cons
    `symbol-several
    (for [form forms]
      `(str '~form " => " ~form "\n"))))

(my.best/disp 
  (map inc [1 2 3])
    (+ 4 5 6))

Two smart guys on Clojurians Slack helped me to write the disp macros.

The defprint macro - by Herwig Hochleitner

The defprint macro defines a function with a tweak: each time the function is called, it prints the values of its arguments. The tricky part is that it works also with desctructuring.

(ns my.best$macros)

(defmacro defprint [func-name args & body]
  `(defn ~func-name [& args#]
       (print '~func-name "called with: " args#)
            (let [~args args#]
                   ~@body)))

(my.best/defprint foo [a b c] (+ a b c))
(foo 1 2 3)

And here is what is printed in when foo is called:

(with-out-str (foo 1 2 3))

Another example, with arguments desctructuring:

(my.best/defprint hello-world [& {:keys [language] 
                                  :or {language :en}}]
                                     (case language
                                                        :fr "bonjour monde"
                                                                           :en "hello world"))

(hello-world :language :fr)
(with-out-str (hello-world :language :fr))

And here is the code generated by the macro:

(macroexpand-1 '(my.best/defprint foo [& {:keys [language]} :or {language :en}]))

Take a couple of minutes to meditate on the power of this part of the macro:

(let [~args args#]
       ~@body)

Herwig Hochleitner helped me to write the defprint macro.

You can read more about desctructuring.

The doseq-indexed macro - by Tim Baldridge

doseq-indexed works like doseq with an additional binding to the index.

Tim Baldridge (core.async author) wrote an elegant implementation for doseq-indexed:

(ns my.best$macros)

(defmacro doseq-indexed [index-sym [item-sym coll] & body]
  `(doseq [[~item-sym ~index-sym]
             (map vector ~coll (range))]
                  ~@body))

(with-out-str
  (my.best/doseq-indexed i [x [10 100 1000]]
                           (println "i: " i "x: " x)))

Here is Tim’s original gist.

Feel free to share your favorite macros in the comments below.

Clojure rocks!