IllegalStateException:尝试在宏中调用未绑定的fn

时间:2014-08-12 06:50:33

标签: macros clojure

我正在尝试编写一个调用某些函数的宏。这些函数只能由宏使用,所以我将它们放在包裹宏的letfn内。伪代码:

(letfn [(fn-a [] ...)
        (fn-b [] ...)
        (fn-c [] (fn-b))]
  (defmacro my-macro [stuff]
   `(let [whatever# (fn-a)]
      (fn-c))))

fn-afn-c的调用有效,但当fn-c尝试调用fn-b时,我得到 IllegalStateException:尝试调用未绑定的fn:#'名称。空间/ FN-b 。那是为什么?

如果我将fn-bfn-c放在他们自己的defn中,那么一切正常。但我不想这样做,因为它不干净。

编辑:为了测试,我尝试将函数绑定放在内部let中,但遇到了同样的异常。

2 个答案:

答案 0 :(得分:2)

我认为这不会起作用。 例如,对fn-c的调用扩展为your.namespace/fn-c,因此您的代码似乎调用恰好具有相同名称的其他函数。 但你没有your.namespace/fn-b,这引起了例外。

要引用不合格的符号,您需要引用它并取消引用它:~'fn-a 但这不起作用,因为本地函数没有在扩展点定义,你只能将它们用于宏本身。

您必须在命名空间中定义函数并在宏中限定它们或将它们包含在宏展开中,这将在每次使用时再次定义它们。

答案 1 :(得分:1)

我不确定这是否正是你所追求的,但如果我这样做:

(letfn [(fn-a [] (println 1))
        (fn-b [] (println 2))
        (fn-c [] (fn-b))]
  (defmacro my-macro [stuff]  
    `(let [whatever# ~(fn-b)]
       ~(fn-c))))

然后它工作 - 只需要波形符号来取消引用函数调用。

以下两项工作:

(defn fn-x [] (println 1))

(defn fn-y [] (fn-x))

(defmacro my-macro2 [stuff]  
        `(let [whatever# (fn-x)]
           (fn-y)))

(defn fn-x [] (println 1))

(defn fn-y [] (fn-x))

(defmacro my-macro2 [stuff]  
        `(let [whatever# ~(fn-x)]
           ~(fn-y)))

在第一种情况下,正在评估函数并将它们的结果在编译时合并到宏中,而在第二种情况下,它们在运行时被评估。使用letfn(它本身就是一个宏),结果在编译时不可用(大概是因为它们是在编译宏之后编译的)所以这些函数只能在运行时使用。