【问题标题】:Why can't you unquote-splice in normal (unquoted) code?为什么不能在正常(未引用)代码中取消引用拼接?
【发布时间】:2012-02-07 03:59:14
【问题描述】:

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

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

【问题讨论】:

  • 可能是为了避免将语言的“元”特性与该语言的“正常”特性混合:)

标签: macros clojure quoting


【解决方案1】:

一个简单的原因是那么

`(+ 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))

【讨论】:

  • 好答案,我喜欢这些例子。谢谢!
【解决方案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])

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

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

这两个例子都很简单,但是syntax-quote和朋友们在写更复杂的宏的时候真的是自己动手了。

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

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

【讨论】:

    猜你喜欢
    • 2018-07-14
    • 2018-01-10
    • 1970-01-01
    • 2021-04-24
    • 2015-12-09
    • 2019-02-16
    • 1970-01-01
    • 2011-02-24
    • 1970-01-01
    相关资源
    最近更新 更多