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?
'(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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With