Clojure宏符号求值

Clojure宏符号求值,clojure,functional-programming,macros,Clojure,Functional Programming,Macros,我对宏中如何计算符号感到困惑。我尝试了下面的例子 (defmacro fx-bad [f x] `(f x)) (defmacro fx [f x] `(~f ~x)) (let [f inc x 1] (fx f x)) ;-> 2 (let [f inc x 1] (fx-bad f x)) ;-> exception fx宏功能正常,而fx bad会引发异常 CompilerException java.lang.RuntimeException: No

我对宏中如何计算符号感到困惑。我尝试了下面的例子

(defmacro fx-bad
  [f x]
  `(f x))

(defmacro fx
  [f x]
  `(~f ~x))

(let [f inc x 1] (fx f x)) ;-> 2
(let [f inc x 1] (fx-bad f x)) ;-> exception
fx宏功能正常,而fx bad会引发异常

CompilerException java.lang.RuntimeException: No such var: user/f, compiling:(/tmp/form-init2718774128764638076.clj:12:18)
符号是否在宏内部解析?为什么fx bad不起作用,但fx起作用

-

编辑:

显然,这个异常与名称空间有关。实际上,宏中从未计算参数~在语法中,quote只生成传递给宏的实际字符串符号,如果没有它,列表中的符号将按原样返回

有趣的是,如果提供给宏调用的参数和quoted not syntax quoted列表中的符号具有相同的名称,则它们不必取消quoted,反正它们是相同的符号。这是一个很好的指示,宏是如何在求值之前发生的,并且只是操纵原始符号,这在这一点上没有任何意义

然而,对于语法,引号大小写是不同的,异常会一直抛出,直到符号被取消引号为止,即使扩展的宏看起来像是计算器要计算的有效代码行。这里有一些例子

(defmacro fx
  [f x]
  `(~f ~x))

(defmacro fx-bad
  [f x]
  '(f x))

(defmacro fx-very-bad
  [f x]
  `(f x))


`(let [f inc x 1] ~(macroexpand '(fx f x)))
`(let [f inc x 1] ~(macroexpand '(fx-bad f x)))
`(let [f inc x 1] ~(macroexpand '(fx-very-bad f x)))

(macroexpand '(fx (fn [a] a) b))
(macroexpand '(fx-bad (fn [a] a) b))
(macroexpand '(fx-very-bad (fn [a] a) b))


(let [f inc x 1] (fx f x)) ;-> 2
(let [ff inc xx 1] (fx ff xx)) ;-> 2
(let [f inc x 1] (fx-bad f x)) ;-> 2
;(let [ff inc xx 1] (fx-bad ff xx)) ;-> exception
;(let [f inc x 1] (fx-very-bad f x)) ;-> exception
-


那么这里到底发生了什么,为什么在syntax quote的情况下抛出异常呢?

请参阅和大多数其他Clojure书籍中的完整细节

基本上,backquote创建一个模板,tilde告诉编译器要替换哪些值。在Groovy、bash和其他语言中,它类似于字符串中变量的替换:

f = "+";
x = 1;
y = 2;
result = "(${f} ${x} y)"

result => "(+ 1 y)"
在该示例中,y未被替换。Clojure宏等价物为:

(let [f  '+
      x  1
      y  2]
  `(~f ~x y) )

;=> (+ 1 y)

因为y不是用波浪号括起来的,所以它是按字面意思取的,不会被变量y的内容替换。

尝试将示例粘贴到repl中,您会注意到其中留下的几个错误:+应该被引用,并且有太多的反勾号。好了,宏发生在读卡器和求值器之间。读卡器只读取此时不绑定到任何东西的符号,它们只是标识符。它构建了与代码相同的数据结构,但元素仍然只是符号。计算器通过解析符号和应用函数递归地计算表单。现在,如果宏发生在这个阶段,将读取器读取的表单替换为其他内容,那么为什么需要在宏内部解析符号,而不仅仅是让计算器解析它们?
(let [f  '+
      x  1
      y  2]
  `(~f ~x y) )

;=> (+ 1 y)