Clojure,在语法quote之外取消quote切片

Clojure,在语法quote之外取消quote切片,clojure,functional-programming,macros,lisp,Clojure,Functional Programming,Macros,Lisp,在clojure中,我们可以使用unquote切片~@来扩展列表。比如说 (macroexpand `(+ ~@'(1 2 3))) 扩展到 (clojure.core/+ 1 2 3) 这是宏中重新排列语法时的有用功能。但是,是否可以在宏之外使用不带引号的切片或熟悉的技术,而不使用eval 以下是使用eval (eval `(+ ~@'(1 2 3))) ;-> 6 但我宁愿这样做 (+ ~@'(1 2 3)) 不幸的是,它抛出了一个错误 IllegalStateExcepti

在clojure中,我们可以使用unquote切片
~@
来扩展列表。比如说

(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 do ['(println "hello") '(println "world")]) ;-> error
起初,我以为
apply
就可以了,函数确实如此

(apply + '(1 2 3)) ; -> 6
但是,宏或特殊窗体的情况并非如此。这对于宏来说是显而易见的,因为它在应用之前已经展开,并且无论如何都必须是表单中的第一个元素。对于特殊的形式,这并不明显,但仍然是有意义的,因为他们不像功能一样是一等公民。例如,下面抛出一个错误

IllegalStateException Attempting to call unbound fn: #'clojure.core/unquote-splicing  clojure.lang.Var$Unbound.throwArity (Var.java:43)
(apply do ['(println "hello") '(println "world")]) ;-> error

是在运行时将列表“应用”到特殊表单的唯一方法,使用unquote切片和
eval

Clojure有一个加载和执行程序的简单模型。稍微简化一下,它是这样的:

  • 读卡器从文本流中读取一些源代码

  • 每次将一个表单传递给编译器

  • 编译器展开它遇到的任何宏

  • 对于非宏,编译器应用各种简单的求值规则(特殊形式的特殊规则、文本自身求值、函数调用的编译等)

  • 对编译后的代码进行评估,并可能更改以下表单使用的编译环境

  • 语法引用是一种阅读器功能。它在读取时被发出列表结构的代码替换:

    ;; 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))))
    
    只有在语法引用块的上下文中,读者才能提供
    ~
    ~@
    这种特殊处理,并且语法引用块总是生成一些表单,这些表单可以调用
    clojure.core
    中的一些seq构建函数,或者由引用的数据组成

    这一切都是上面列表中步骤1的一部分。因此,要使syntax quote作为一种类似于
    apply
    的机制发挥作用,您需要它在流程中的该点以正确的形状生成代码,然后在后续步骤中看起来像所需的“
    apply
    result”。如上所述,syntax quote总是生成创建列表结构的代码,特别是它从不返回看起来像unquote
    do
    s或
    if
    s等的unquote表达式,所以这是不可能的

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

    顺便说一句,在您的示例中,
    macroexpand
    调用实际上是多余的,因为引用的语法形式已经与其macroexpansion相同(应该如此,因为
    +
    不是宏):