为什么不能在普通(未引用的)代码中取消引用拼接?

时间:2012-02-07 03:59:14

标签: macros clojure quoting

在Clojure中,您可以取消引用拼接值列表以生成代码,例如

(def extra-values [1 2 3 4])

`(+ 100 200 ~@extra-values)
=> (clojure.core/+ 100 200 1 2 3 4)

似乎合乎逻辑的是,相同的方法应该在不带引号的上下文中起作用,例如

(def extra-values [1 2 3 4])

(+ 1000 ~@extra-values)
=> [an error, but one could argue that the answer should be 1010??]

为什么这不起作用,有没有深层次的技术/哲学原因?

2 个答案:

答案 0 :(得分:13)

一个简单的原因是

`(+ 100 200 ~@extra-values) 

定义不明确:它是否扩展到

(+ 100 200 1 2 3 4)

(+ 100 200 ~@extra-values)

?如果~@构造在该上下文中是合法的,则两者都是有效的解释。

它还会导致宏系统出现严重问题。考虑

(defmacro foo [x & args]
  `(list ~x ~(count args)))

(let [data '(1 2 3)]
  (foo "three" ~@data))

foo如何知道传递的是什么?它当然不能在编译时扩展它,所以现在unquote-splicing只在某些非引用的上下文中有效。

总的来说,它只是混淆了语言以适应对核心概念的不理解 - 如果你真的想要取消引用拼接来构建一个复杂的参数列表,你可以轻松地使用apply之类的东西,比如

(apply + `(100 ~@extra-values 200))

答案 1 :(得分:2)

syntax-quoteunquoteunquote-splicing的目的是帮助开发人员编写宏。

例如,如果没有syntax-quoteunquote,则必须编写

user=> (list :a extra-values)
(:a [1 2 3 4])

而不是:

user=> `(:a ~extra-values)
(:a [1 2 3 4])

在前一种情况下,读者(人类读者 - 而非复制者)更难理解结果形式的样子,而后一种情况则维持所得形式的“形状”。

那么如果不是vector [1 2 3 4]我们想将extra-values的内容作为元素拼接到结果表单中呢?我们需要unquote-splicing,以便我们可以写:

user=> `(+ 100 200 ~@extra-values)
(clojure.core/+ 100 200 1 2 3 4)

而不是:

user=> (concat `(+ 100 200) extra-values)
(clojure.core/+ 100 200 1 2 3 4)

同样,unquote-splicing版本允许代码在评估代码时类似于结果形式的“形状”,而在后一版本中,“形状”在apply的噪声中丢失和list

这两个例子都非常简单,但是syntax-quote和朋友们在编写更复杂的宏时真的很自负。

回到你的问题,为什么你不能写(+ 1000 ~@extra-values)?我们已经在apply中具有该功能(还有一些限制):

user=> (apply + 1000 extra-values)
1010