定义Clojure宏语法

时间:2010-06-04 21:23:42

标签: clojure

我将unless宏定义如下:

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

你可以看到它运作良好。

现在,在Clojure中,可以通过两种方式定义列表:

; create a list
(list 1 2 3)

; shorter notation
'(1 2 3)

这意味着可以在没有unless关键字的情况下编写list宏。但是,这会导致抛出Java异常:

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

有人可以解释为什么会失败吗?

1 个答案:

答案 0 :(得分:15)

'(foo bar baz)不是(list foo bar baz)的快捷方式,它是(quote (foo bar baz))的快捷方式。虽然列表版本将返回包含变量foo,bar和baz的值的列表,但带有'的版本将返回包含符号foo,bar和baz的列表。 (换句话说,'(if expr nil body)(list 'if 'expr 'nil 'body)相同。

这会导致错误,因为使用引用的版本宏会扩展为(if expr nil body)而不是(if (= 1 2) nil (println "Yo"))(因为它不会将宏的参数替换为expr和body,而只返回名称expr和body (然后在扩展代码中将其视为不存在的变量)。

在宏定义中有用的快捷方式是使用``的作用类似于'(即它引用了后面的表达式),但它允许您使用~评估一些未引用的子表达式。例如,您的宏可以重写为(defmacro unless [expr body] `(if ~expr nil ~body))。这里重要的是,exprbody不加引号~。这样,扩展将包含其值,而不是字面上包含名称exprbody