31 Mar 2012

Here’s a quick little Clojure1 macro I’ve started using.

The Book Feed is a new website I’ve put together where you can write short book reviews, and optionally post them to Facebook. When testing, I don’t want the post-to-Facebook code to run while I’m developing, or my Facebook account would fill up with test-data. It should really only run in production.

That’s straightforward to code in any language. I write some kind of isLive() function, then wrap the relevant code in if ( isLive() ) {.... }. Easy, right?

Well, in Clojure we can go one better. Take a look at this code:

  (defn is-live? [] ...)
  (defmacro if-live [function & args]
    `(if (is-live?)
       (~function ~@args)
       (println "DEVELOPMENT Skipping:" '~function ~@args)))

…then later in my code I replace:

(post-to-facebook data1 data2 ...)

…with:

  (if-live post-to-facebook data1 data2 ...)

At first glance this looks like syntactic sugar. It’s super-short, but is it really any better than what we’d say in something like Java?

  if (isLive()) {
      postToFacebook(data1, data2, ...);
  } else {
      log.debug("DEVT Skipping: " + data1 + " " + data2 ...);
  }

Well, yes. This macro is significantly better, and not just because of all the typing it saves. The if-live macro has a magic property we’d struggle to get in other languages: Instead of just skipping the code, or printing a generic debugging message, it prints the exact code and arguments it would execute were we in production. In other languages we’re stuck with the tedious problem of keeping the log message & the call in sync with each other2.

But because Clojure code can be treated as regular data, it’s trivial to print the call instead of executing it. With no effort, I can see exactly what the call would send to Facebook, without worrying about keeping the debug message in sync with the function call.

Yay macros.

  1. Everything in this post is applicable to LISPs in general. 

  2. I can think of a few workarounds for a couple of languages, but nothing that comes close to the elegance of this macro. Nothing that says, “Add these 8 characters to any function and it just works.” 

comments powered by Disqus