5

在 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??]

这行不通有什么深刻的技术/哲学原因吗?

4

2 回答 2

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))
于 2012-02-07T04:51:33.627 回答
2

syntax-quoteunquote和的意义unquote-splicing在于帮助开发人员编写宏。

例如,如果没有syntax-quoteunquote你将不得不写

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

代替:

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

在前一种情况下,读者(人类读者 - 而不是 repl)更难理解结果形式的外观,而后一种情况保持结果形式的“形状”。

那么如果不是vector [1 2 3 4]我们想要将extra-valuesas 元素的内容拼接到结果表单中呢?我们需要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版本允许代码在评估代码时类似于结果表单的“形状”,而在后一个版本中,“形状”会在 and 的噪音中apply丢失list

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

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

user=> (apply + 1000 extra-values)
1010
于 2012-02-09T15:29:04.827 回答