在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??]
为什么这不起作用,有没有深层次的技术/哲学原因?
答案 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-quote
,unquote
和unquote-splicing
的目的是帮助开发人员编写宏。
例如,如果没有syntax-quote
和unquote
,则必须编写
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