Clojure,在语法quote之外取消quote切片
在clojure中,我们可以使用unquote切片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
~@
来扩展列表。比如说
(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总是生成创建列表结构的代码,特别是它从不返回看起来像unquotedo
s或if
s等的unquote表达式,所以这是不可能的
这不是问题,因为考虑到上述执行模型,代码转换是合理的,可以使用宏来实现
顺便说一句,在您的示例中,macroexpand
调用实际上是多余的,因为引用的语法形式已经与其macroexpansion相同(应该如此,因为+
不是宏):