Tech behind Tech

Raw information. No finesse :)

Clojure Macros Simplified

with 5 comments


One of the powerful features of clojure ( or any other LISP) is macros. Macros are so powerful that will allow you do things that you can never do in any other language. There is a classic example of implementing “unless” functionality using macros. I am not going to talk about why we need macros and when to use them. There is so much literature out there discussing about Macros. Also checkout Clojure in Action book for in depth look on Macros. I have read both Joy of Clojure and Clojure in Action, when it comes to Macros I will recommend Clojure in Action. The idea behind this blog to simplify macros as much as possible.

As you know in Clojure code is data and data is code. Our code is a clojure List. So we can programmatically create a list and execute it.

user> (def my-code '(println "techbehindtech"))
#'user/my-code
user> my-code
(println "techbehindtech")
user> (eval my-code)
techbehindtech

In above example, we created a list my-code. This list could be created dynamically. And using eval function we could execute that list as a clojure code.

So to create code in clojure, all we have to do is create a clojure list. In clojure, before a code is evaluated we have hook to introduce our own code using Macros system.

In Macros we are basically creating a list of code similar to my-code. Lets create a simple macro that will allow us to write functions with some log message.

(defmacro def-logged-fn [fn-name args & body]
        `(defn ~fn-name ~args
           (println "Calling ...")
           ~@body))
user> (def-logged-fn say[name]
        (println (str "hello " name)))
#'user/say
user> (say "siva")
Calling ...
hello siva
nil

macroexpand and macroexpand-1:

macroexpand and macroexpand-1 are useful functions to know about when using
writing macros. These functions expand our macro forms.

user> (macroexpand-1 '(def-logged-fn say[name]
        (println (str "hello " name))))

(clojure.core/defn
 say
 [name]
 (clojure.core/println "Calling ...")
 (println (str "hello " name)))

You can see that our macro created a fn called “say” that calls println first before the original body. That is pretty cool huh.

The difference between macroexpand-1 and macroexpand is macroexpand-1
will expand only one level of macros and macroexpand calls macroexpand-1
until all macro forms are expanded.

The same macro expanded with macroexpand will look like

(macroexpand '(def-logged-fn say[name]
        (println (str "hello " name))))

(def
 say
 (clojure.core/fn
  ([name]
   (clojure.core/println "Calling ...")
   (println (str "hello " name)))))

As you can see macroexpand expanded defn macro also.

Coming back to our macro, there are 3 reader macros that we have used

- Backtick `
– Tilda ~
– Tilda Ampersand ~@

Backtick ` (syntax-quote):

This is called as syntax-quote. This quotes the whole expression that way you do not have to quote each one of them

Tilda ~ (unquote):

This is called as unquote. This unquotes (substitutes) the value. For
instance, we wanted to replace fn-name with its value, so we tilda
before fn-name.

Tilda Ampersand ~@ (unquote-splicing):

It is easy to show with an example. Lets take our macro that we wrote and change unquote-splicing to unquote before body and let us what happens.

user> (defmacro def-logged-fn [fn-name args & body]
        `(defn ~fn-name ~args
           (println "Calling ...")
           ~body))
#'user/def-logged-fn
user> (macroexpand-1 '(def-logged-fn say[name]
        (println (str "hello " name))))

(clojure.core/defn
 say
 [name]
 (clojure.core/println "Calling ...")
 ((println (str "hello " name))))

You can see the last line looks wierd. It is trying to call output of
println as a function. This will obiviously fail.


user> (def-logged-fn say[name]
         (println (str "hello " name)))
#'user/say
user> (say "techbehindtech")
Calling ...
hello techbehindtech
; Evaluation aborted.

No message.
  [Thrown class java.lang.NullPointerException]

As body is a list ( we are using varible args) when we just unquote it
is putting body inside a single list. To avoid that we have to
unquote-splicing.

Auto-gensym

Inside a macro, sometimes you want to create unqualified symbol to use
in a let block. We can do this easily by appending # in end of symbol.

Written by Siva Jagadeesan

September 28, 2010 at 4:32 pm

Posted in Clojure

Tagged with , , , ,

5 Responses

Subscribe to comments with RSS.

  1. Thanks for this nice and clear explanation!

    Vic

    September 29, 2010 at 6:39 pm

  2. Thank you, I need to read a lot of macro examples to understand it.

    adria

    January 9, 2012 at 6:33 pm

  3. ‘@’ is not an “ampersand”.
    ‘&’ is an “ampersand”.

    ramy

    February 19, 2013 at 9:32 pm

  4. nice article!

    rc

    March 26, 2013 at 2:34 pm


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 )

Google+ photo

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

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 146 other followers

%d bloggers like this: