Macros IllegalStateException:试图在宏中调用未绑定的fn
我正试图编写一个宏来调用一些函数。这些函数只能由宏使用,因此我将它们放在包装宏的Macros IllegalStateException:试图在宏中调用未绑定的fn,macros,clojure,Macros,Clojure,我正试图编写一个宏来调用一些函数。这些函数只能由宏使用,因此我将它们放在包装宏的letfn中。伪代码: (letfn [(fn-a [] ...) (fn-b [] ...) (fn-c [] (fn-b))] (defmacro my-macro [stuff] `(let [whatever# (fn-a)] (fn-c)))) 对fn-a和fn-c的调用起作用,但是当fn-c尝试调用fn-b时,我得到非法状态异常:尝试调用未绑定的fn
letfn
中。伪代码:
(letfn [(fn-a [] ...)
(fn-b [] ...)
(fn-c [] (fn-b))]
(defmacro my-macro [stuff]
`(let [whatever# (fn-a)]
(fn-c))))
对fn-a
和fn-c
的调用起作用,但是当fn-c
尝试调用fn-b
时,我得到非法状态异常:尝试调用未绑定的fn:#'name.space/fn-b。为什么呢
如果我把fn-b
和fn-c
放在它们自己的defn
s中,一切正常。但我不想那样做,因为它不干净
编辑:只是为了测试,我尝试将函数绑定放在内部
let
中,但遇到了相同的异常。我不确定这是否正是您想要的,但如果我这样做:
(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)))
及
在第一种情况下,将在编译时对函数进行求值,并将其结果合并到宏中,而在第二种情况下,将在运行时对函数进行求值。使用
letfn
(它本身就是一个宏),结果在编译时不可用(可能是因为它们是在编译宏之后编译的),因此函数只能在运行时使用。我认为这根本不可能奏效。
例如,对fn-c
的调用被扩展为your.namespace/fn-c
,因此您的代码似乎调用了恰好具有相同名称的其他函数。
但是您没有一个您的.namespace/fn-b
,这会引发异常
要引用非限定符号,您需要对其进行引用和取消引用:~'fn-a
但这也行不通,因为本地函数在扩展时没有定义,您只能将它们用于宏本身
您必须在名称空间中定义函数并在宏中限定它们,或者在宏扩展中包含它们,这将在每次使用时再次定义它们。我尝试将函数绑定放在内部
let
中,但遇到了相同的异常。所以我不认为letfn
有错。我也确信这些名字在代码中的其他地方不存在。如果你把它们放在内部let中,你应该使用gensym名字,只需在它们的名字后面加上一个磅符号,例如:fn-a.
。这样,名称将自动生成,并且不会与其他名称冲突。如果你在其他地方没有这些名字的函数,我真的很想知道以前是怎么调用它们的。如果您只想隐藏helper函数,可以使用defn-
将它们设置为当前命名空间/文件的私有函数。要调试宏的任何问题,请始终查看扩展的代码。对于这种用法,macroexpand-1
、macroexpand
和clojure.walk/macroexpand all
顺便说一句。将定义放在何处并不重要,如果您仅通过编写它们的名称来调用它们,这些调用仍然会扩展到名称空间限定的名称。使用macroexpand,您将看到发生了什么(macroexpand-1’(我的宏(stuff…)
我认为最干净的方法就是将FN放入宏扩展中,所以我就是这么做的。谢谢你不是把它倒过来了吗?在第一种情况下,函数在运行时进行评估,在第二种情况下在编译时进行评估。我尝试将函数绑定放在内部let
中,但遇到了相同的异常。所以我不认为letfn
有错。
(defn fn-x [] (println 1))
(defn fn-y [] (fn-x))
(defmacro my-macro2 [stuff]
`(let [whatever# ~(fn-x)]
~(fn-y)))