13

I defined an unless macro as follows:

user=> (defmacro unless [expr body] (list 'if expr nil body))
#'user/unless
user=> (unless (= 1 2) (println "Yo"))
Yo

As you can see it works fine.

Now, in Clojure a list can be defined in two ways:

; create a list
(list 1 2 3)

; shorter notation
'(1 2 3)

This means that the unless macro can be written without the list keyword. However, this results in a Java exception being thrown:

user=> (unless (= 1 2) (println "Yo"))
java.lang.Exception: Unable to resolve symbol: expr in this context

Can someone explain why this fails?

1
  • 6
    FYI, Clojure already has similar macros in core, called when-not and if-not. Commented Jun 4, 2010 at 22:13

1 Answer 1

15

'(foo bar baz) is not a shortcut for (list foo bar baz), it's a shortcut for (quote (foo bar baz)). While the list version will return a list containing the values of the variables foo, bar and baz, the version with ' will return a list containing the symbols foo, bar and baz. (In other words '(if expr nil body) is the same as (list 'if 'expr 'nil 'body).

This leads to an error because with the quoted version the macro expands to (if expr nil body) instead of (if (= 1 2) nil (println "Yo")) (because instead of substituting the macro's arguments for expr and body, it just returns the name expr and body (which are then seen as non-existent variables in the expanded code).

A shortcut that's useful in macro definitions is using `. ` works like ' (i.e. it quotes the expression following it), but it allows you to evaluate some subexpressions unquoted by using ~. For example your macro could be rewritten as (defmacro unless [expr body] `(if ~expr nil ~body)). The important thing here is that expr and body are unquoted with ~. This way the expansion will contain their values instead of literally containing the names expr and body.

Sign up to request clarification or add additional context in comments.

3 Comments

Furthermore ` will take care that the macro is hygienic. list should never ever be used to define macro expansions.
And a short comment for the macro itself: you can support implicit do to allow multiple forms in the macro body. (defmacro unless [expr & body] `(if ~expr nil (do ~@body)))
using list is good if most of your forms would be escaped; (defmacro my-if [pred then else] `(if ~pred ~then ~else)) vs (defmacro my-if [pred then else] (list `if pred then else)), for example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.