Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between using list and back tick in macros

Tags:

clojure

At a conceptual level a macro in LISP (and dialects) take a piece of code (as list) and returns another piece of code (again as list).

Based on above principle a simple macro could be:

(defmacro zz [a] (list print a))
;macroexpand says : (#<core$print clojure.core$print@749436> "Hello")

But in clojure this can also be written as:

(defmacro zz [a] `(print ~a))
;macroexpand says : (clojure.core/print "Hello")

I am not exactly sure about the difference here and which should be the preferred way. The first one look simple as we are supposed to return list and avoid using weird characters like back tick.

like image 651
Ankur Avatar asked Feb 23 '12 17:02

Ankur


4 Answers

No one has pointed this out yet...the difference between your 2 macros is this: your second form (using backtick)

(defmacro zz [a] `(print ~a))

is equivalent to:

(defmacro zz [a] (list 'print a))

Which is different from your first example:

(defmacro zz [a] (list print a))

Note the missing single quote -- that is why your macroexpand is different. I agree with the other people posting: using backquote is more conventional if your macro has a fairly simple 'shape'. If you have to do code walking or dynamic construction (i.e. a complex macro), then using lists and building it up is often what's done.

I hope this explanation makes sense.

like image 155
Kyle Burton Avatar answered Nov 17 '22 02:11

Kyle Burton


Constructing lists explicitly is "simplest", in a way, because there are few core concepts you need to know: just accept a list and change it around till you have a new list. Backtick is a convenient shortcut for "templating" chunks of code; it is possible to write any macro without it, but for any large macro it quickly becomes very unpleasant. For example, consider two ways of writing let as a macro over fn:

(defmacro let [bindings & body]
  (let [names (take-nth 2 bindings)
        vals (take-nth 2 (rest bindings))]
    `((fn [~@names]
        (do ~@body))
      ~@vals)))

(defmacro let [bindings & body]
  (let [names (take-nth 2 bindings)
        vals (take-nth 2 (rest bindings))]
    (cons (list `fn (vec names) (cons `do body))
          vals)))

In the first case, using backtick makes it fairly clear that you're writing a function of the names containing the body, and then calling it with the values - the macro code is "shaped" the same as the expansion code, so you can imagine what it will look like.

In the second case, with just cons and list all over the place, it is a real headache to work out what the expansion will look like. This isn't always the case, of course: sometimes it can be clearer to write something without a backtick.

Another very important point was made by Kyle Burton: print is not the same as 'print! Your macro expansion should contain the symbol print, not its value (which is a function). Embedding objects (such as functions) in code is very fragile and only works by accident. So make sure your macros expand to code you could actually have written yourself, and let the evaluation system do the hard work - you could type in the symbol print, but you couldn't type in a pointer to the current value of the function print.

like image 35
amalloy Avatar answered Nov 17 '22 00:11

amalloy


There's a style difference between them. Your example is very simple but in more complex macros the difference will be bigger.

For example the unless macro as defined in "The Joy of Clojure" book:

(defmacro unless [condition & body]
    `(if (not ~condition)
        (do ~@body)))

From the book:

Syntax-quote allows the following if-form to act as a sort of template for the expression that any use of the macro become when it is expanded.

When creating a macro always choose the most readable and idiomatic style.

To contrast, the above code can equivalently be written like so:

(defmacro unless [condition & body]
  (list 'if (list 'not condition)
            (list* 'do body)))
like image 5
islon Avatar answered Nov 17 '22 01:11

islon


In my experience they are equivalent. Though there may be some edge cases I'm not aware of.

@islon 's example can equivalently be written as:

To contrast, the above code can equivalently be written like so:

(defmacro unless [condition & body]
  (list 'if (list 'not condition)
            (list* 'do body)))
like image 1
Alex Baranosky Avatar answered Nov 17 '22 00:11

Alex Baranosky