Why does not Clojure support private functions in macro?
我正在尝试实现xor宏,并提出了一个问题。
我无法在宏中使用私有函数。
这里是示例:
私有功能
1 2 3 4 5 | (defn :^private xor-result [x y] (if (and x y) false (or x y))) |
宏
1 2 3 4 5 6 7 8 9 | (defmacro xor ([] true) ([x] x) ([x & next] `(let [first# ~x second# ~(first next)] (if (= (count '~next) 1) (xor-result first# second#) (xor (xor-result first# second#) ~@(rest next)))))) |
这是错误:
1 | CompilerException java.lang.IllegalStateException: var: #'kezban.core/xor-result is not public |
当我删除^:private标志时,问题解决了。
问题是:这种行为的原因是什么?
更新:我可以通过以下方法使用私有函数。
私有功能
1 2 3 4 5 | (defn ^:private xor-result [x y] (if (and x y) false (or x y))) |
新宏
1 2 3 4 5 6 7 8 9 10 | (defmacro xor ([] true) ([x] x) ([x & next] (let [first x second `(first '(~@next)) result (xor-result (eval first) (eval second))] `(if (= (count '~next) 1) ~result (xor ~result ~@(rest next)))))) |
如果您在
1 2 3 4 5 | (ns ns1) (defn- my-fun [x] (first x)) (defmacro my-macro [x] (my-fun ~x)) |
并在另一个命名空间中使用它:
1 2 3 4 | (ns ns2 (:require [ns1 :refer [my-macro]])) (my-macro [1 2]) |
编译器将在编译阶段调用宏,并将在
1 2 3 4 | (ns ns2 (:require [ns1 :refer [my-macro]])) (ns1/my-fun [1 2]) |
,此代码最终将编译为字节码。
如您所见,编译器将在
要调试宏,可以使用
您还需要记住,您的宏适用于程序数据:代表代码的数据结构(符号,列表,向量等)。例如,在您的宏的第二个版本中,它按原样运行符号,而不是绑定到它们的运行时值:
1 2 3 4 5 | (macroexpand '(xor true false)) ;; => (if (clojure.core/= (clojure.core/count (quote (false))) 1) true (boot.user/xor true)) (macroexpand '(xor (zero? 1) (zero? 0))) ;; => (if (clojure.core/= (clojure.core/count (quote ((zero? 0)))) 1) false (boot.user/xor false)) |
如您所见,您的
如果您确实想从将由其他命名空间使用的公共宏中访问私有变量,则可以使用一种技巧。
当通过在代码中引用var的值来解析它的值时,Clojure会检查var是公共的还是私有的,并且如果您尝试访问私有的var,则编译器会抱怨。但是,您可以使用
因此,假设函数
1 | (#'mynamespace.core/xor-result first# second#) |