Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Macros IllegalStateException:试图在宏中调用未绑定的fn_Macros_Clojure - Fatal编程技术网

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)))