为什么Clojure不支持宏中的私有函数?

Ert*_*tin 4 lisp macros functional-programming clojure

我试图实现xor宏并想出了一个问题.

我无法在宏中使用私有函数.

这是一个例子:

私人职能

(defn :^private xor-result
  [x y]
  (if (and x y)
    false
    (or x y)))
Run Code Online (Sandbox Code Playgroud)

(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))))))
Run Code Online (Sandbox Code Playgroud)

这是错误:

CompilerException java.lang.IllegalStateException: var: #'kezban.core/xor-result is not public
Run Code Online (Sandbox Code Playgroud)

当我删除^:private flag 时问题解决了.

问题是:这种行为的原因什么?


更新:我可以使用以下方法的私有函数.

私人职能

(defn ^:private xor-result
  [x y]
  (if (and x y)
    false
    (or x y)))
Run Code Online (Sandbox Code Playgroud)

新宏

(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))))))
Run Code Online (Sandbox Code Playgroud)

Pio*_*dyl 5

如果你有一个宏ns1:

(ns ns1)

(defn- my-fun [x] (first x))

(defmacro my-macro [x] (my-fun ~x))
Run Code Online (Sandbox Code Playgroud)

并在另一个命名空间中使用它:

(ns ns2
  (:require [ns1 :refer [my-macro]]))

(my-macro [1 2])
Run Code Online (Sandbox Code Playgroud)

编译器将在编译阶段调用宏,它将在ns2命名空间中生成代码,并将变为:

(ns ns2
  (:require [ns1 :refer [my-macro]]))

(ns1/my-fun [1 2])
Run Code Online (Sandbox Code Playgroud)

并且此代码最终将编译为字节代码.

正如您所看到的,编译器将ns1ns2命名空间中看到私有函数的使用,并会抱怨它.

要调试宏,您可以使用它macroexpand来查看应用宏的结果.

您还需要记住,您的宏可以处理您的程序数据:表示代码的数据结构(符号,列表,向量等).例如,在宏的第二个版本中,它按原样运行符号,而不是绑定到它们的运行时值:

(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))
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,您的xor-result函数不会使用实际运行时值调用,而是使用表示代码的数据调用.xor-result在编译期间直接在宏中调用.在宏的第一个版本中,它在宏生成的代码中使用,在编译期间不会被调用.