Clojure,在语法引用之外取消引用切片

时间:2017-05-18 15:35:14

标签: clojure functional-programming macros lisp

在clojure中,我们可以使用非引号切片~@来传播列表。例如

(macroexpand `(+ ~@'(1 2 3))) 

扩展为

(clojure.core/+ 1 2 3)

重新排列语法时,这在宏中非常有用。但是有可能在宏之外使用非引用切片或熟悉的技术,而没有 eval吗?

以下是eval

的解决方案
(eval `(+ ~@'(1 2 3))) ;-> 6

但我宁愿做

(+ ~@'(1 2 3))

不幸发生了错误

IllegalStateException Attempting to call unbound fn: #'clojure.core/unquote-splicing  clojure.lang.Var$Unbound.throwArity (Var.java:43)

起初我以为apply会这样做,而功能

确实如此
(apply + '(1 2 3)) ; -> 6

但是,宏或特殊形式不是这种情况。宏显而易见,因为它在应用之前已经扩展,并且无论如何必须是表单中的第一个元素。虽然有特殊形式,但它并不那么明显,但仍然有意义,因为他们不像职能那样是一等公民。例如,以下引发错误

(apply do ['(println "hello") '(println "world")]) ;-> error

是#34;应用"的唯一方法在运行时列出特殊形式以使用非引号切片和eval

1 个答案:

答案 0 :(得分:7)

Clojure有一个简单的模型,说明如何加载和执行程序。略有简化,它是这样的:

  1. 读者从文本流中读取一些源代码;

  2. 一次将一个表单传递给编译器;

  3. 编译器扩展它遇到的任何宏;

  4. 对于非宏,编译器应用各种简单的评估规则(特殊形式的特殊规则,文字评估给自己,函数调用如此编译等);

  5. 评估已编译的代码,并可能更改以下表单使用的编译环境。

  6. 语法quote是一个读者功能。它在读取时由发出列表结构的代码替换:

    ;; note the ' at the start
    user=> '`(+ ~@'(1 2 3))
    (clojure.core/seq
      (clojure.core/concat (clojure.core/list (quote clojure.core/+)) (quote (1 2 3))))
    

    只有在语法引用的块的上下文中,读者才能提供~~@这种特殊处理,并且语法引用的块总是生成可以调用少数seq-building的形式函数来自clojure.core,并且由引用数据组成。

    这一切都是从上面列表中的第1步开始的。因此,对于类似apply的机制,语法引用非常有用,您需要它在流程中的那一点生成正确形状的代码,然后看起来像所需的“apply结果“在后续步骤中。如上所述,syntax-quote总是生成创建列表结构的代码,特别是它永远不会返回看起来像不带引号的doif等的不带引号的表达式,所以这是不可能的。

    这不是问题,因为给定上述执行模型的合理代码转换可以使用宏来实现。

    顺便提一下,macroexpand调用在您的示例中实际上是多余的,因为语法引用的表单 与其宏展开相同(因为它应该是,因为{{1} }不是宏):

    +