When do you need macros in Clojure?

Posted: January 9, 2013 in coding, macros
Tags: , ,

“The first rule of macro club is: Don’t write macros”

I wrote this originally as an answer to a StackOverflow question. But the question got closed by some trigger-happy mods before I clicked post. How unfriendly!

Anyway, in case it is useful to people wondering when they should and shouldn’t use macros, I thought I would post it here instead….

Functions vs. Macros

I often get questions about when you should use functions vs. macros in Clojure / other Lisps. The rule is simple “don’t use macros unless you have to”. This post is about those cases where you have to use macros in Clojure, i.e. when functions cannot do the job.

The fundamental limitation with functions is that the rule for function application is fixed: at runtime, function parameters get eagerly evaluated in turn, and then the function itself is called with these parameters.

If you are happy with this approach then functions are fine, but there are cases where it won’t work. You need a macro if you want to get around one of the limitations of function application, for example:

  • You don’t want to eagerly evaluate all arguments (e.g. in a **control structure**)
  • You want some of the code to **run at compile time**, not at runtime
  • You want a **custom syntax** which can’t be evaluated with the normal function evaluation rule – so you need to transform your custom syntax into something that can be executed first

Examples

Here are quick  illustrations of these cases with Clojure.

Creating control structures: you can’t do this with a function, because you need to do the test before deciding whether or not to evaluate the body. Since functions always evaluate all their arguments, you can’t do this with a function:

(defmacro when-negative [test & body]
  `(if (< ~test 0) (do ~@body)))

(when-negative -1 (println "Haz a negative number!"))
=> "Haz a negative number!"

Performing compile-time computation: if you compute a value in a macro, it will get turned into a constant at runtime. This can be helpful for various optimisations, for example the following macro will compute a constant at compile time:

(defmacro constant [body]
  (eval body))

(defn millis-since-compilation []
  (- (System/currentTimeMillis) (constant (System/currentTimeMillis))))

(millis-since-compilation)
=> 9196

Providing custom syntax: For example new binding forms that bind a value to a symbol in a loop. (compare to how long it took for Java to get a new `for (x : collection)` syntax, in Lisp/Clojure it’s a five-liner…..)

(defmacro for-loop [[sym init check change] & code]
 `(loop [~sym ~init value# nil]
    (if ~check
      (recur ~change (do ~@code))
      value#)))

(for-loop [i 0 (< i 10) (inc i)] (print i))
=> 0123456789

Final thoughts…

Note that all of the above examples are quite special cases. Normal code shouldn’t need macros. You should normally start off with a function, and only reach for a macro when all else fails. Macros are more complex to write, harder to maintain and can’t be used flexibly in ways that functions can (e.g. passing to higher order functions).

Advertisement
Comments
  1. Nikita Beloglazov says:

    Actually, you can do control structures with functions but you have to wrap all arguments to function invocations. When we need to get value of some argument – we invoke function. It is looks uglier but possible with functions.

    • mikera7 says:

      Interesting point.

      I guess it depends on your definition but I wouldn’t personally regard that as a control structure (at least not in the sense of a language-level control structure).

      The function approach is really “simulating” a control structure with higher order functions. It’s not actually changing the control flow itself (i.e. the parameter representing your function still gets computed and passed to your higher order function in normal execution order).

      Which you can of course do this simulation trick in any Turing complete language – I’ve written similar stuff in Java to achieve complex flow control which was amusingly convoluted but worked pretty well in the end.

      • Alex Stoddard says:

        In a sense aren’t all created control structures just a subset of the “custom syntax” class, with such macros creating syntactic sugar around combinations of the “if” special form?

        After all, “if” is a special form because flow control is a special case – neither functions nor macros can control flow without “if” or abstractions built on top of it.

      • mikera7 says:

        Yeah that’s a good observation. I drew control structures out as a special case because it is a particularly important one. All macros are ultimately just an application of “do code transformation and computation at compile time” so I guess you could say everything is custom syntax. Even the constant compilation stuff is really just custom syntax for expressing a constant as the result of a computation :-)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s