关于clojure:引号内的unquote剪接会导致找不到符号错误

unquote-splice within quote causes symbol-not-found error

这是代码:

1
2
3
(defmacro inspect-caller-form [& args]
  {:form (quote `(inspect-caller-form ~@args))})
(inspect-caller-form 1 2 3)

和错误:

1
CompilerException java.lang.RuntimeException: Unable to resolve symbol: args in this context, compiling:(/Users/kaiyin/personal_config_bin_files/workspace/cina/src/cina/ref_types.clj:406:5)

但是如果我将语法引号移到quote之前,事情似乎就起作用了:

1
2
3
(defmacro inspect-caller-form [& args]
  {:form `(quote (inspect-caller-form ~@args))})
(inspect-caller-form 1 2 3)

为什么?


它通常可以通过简单地在repl上评估语法引用问题并查看其评估结果的形式来帮助调试语法引用问题。在这种情况下,我们可以看到第一个示例扩展为读取语法引用的未评估结果。这是因为语法引号是clojure中为数不多的读取器宏之一,并且在宏扩展时间开始时就开始执行它。然后,您调用报价会阻止它进一步扩展。因此语法引用永远无法完成它的工作。

1
2
3
4
5
6
7
user> (let [args [1 2 3]]
        (quote `(inspect-caller-form ~@args)))

(clojure.core/seq (clojure.core/concat
                      (clojure.core/list
                          (quote user/inspect-caller-form))
                      args))

第二个表达式允许语法引用形式完成评估,并生成结果作为引用调用。因此,最终结果是一个单引号列表,其中的值已经被拼接到其中。

1
2
3
4
user> (let [args [1 2 3]]
        `(quote (inspect-caller-form ~@args)))

(quote (user/inspect-caller-form 1 2 3))

在语法引号中使用引号实际上是一个有用的技巧,用于编写故意将新符号引入调用者范围的宏,这称为符号捕获。