Clojure 使用前向声明的宏时参数数错误
我在宏中有一个函数my plus,它包含一个名为my macro的宏。但是我的宏是在我的plus宏定义之后声明的。我使用声明以避免无法解决符号错误 我在repl中键入这些代码Clojure 使用前向声明的宏时参数数错误,clojure,Clojure,我在宏中有一个函数my plus,它包含一个名为my macro的宏。但是我的宏是在我的plus宏定义之后声明的。我使用声明以避免无法解决符号错误 我在repl中键入这些代码 (declare my-macro my-plus) (defn my-plus-in-macro [x y] (my-macro (my-plus x y))) (defmacro my-macro [my-fn] `~my-fn) (defn my-plus [x y] (+ x y)
(declare my-macro my-plus)
(defn my-plus-in-macro [x y]
(my-macro (my-plus x y)))
(defmacro my-macro [my-fn]
`~my-fn)
(defn my-plus [x y]
(+ x y))
然后,如果执行这个,我希望得到3
(my-plus-in-macro 1 2)
但是我得到的是
ArityException Wrong number of args (1) passed to: user$my-macro clojure.lang.AFn.throwArity (AFn.java:437)
但是,如果我在repl中再次在宏定义中执行这个加号
(defn my-plus-in-macro [x y]
(my-macro (my-plus x y)))
然后我执行这个
(我在宏1和宏2中的加号)
正是我所期望的,3
在我第一次执行(宏1 2中的加号)时发生了什么
不知何故,当我第一次在宏中定义加号时,clojure看到了我的宏符号,但它还不知道我打算将其用作宏的名称
虽然在宏中定义加号之后,我就将宏定义为符号,但clojure仍然不知道我的宏是宏的符号。这就是为什么我有算术例外
但是,不知何故,当我用相同的代码重新定义我的plus in宏时,Clojure现在知道我的宏是一个宏,并将其视为一个宏,然后我的代码就可以工作了
当然,如果我在宏定义中将宏定义放在加号之前,我不会得到任何异常。但这是否意味着我不能使用declare为宏保留符号
编辑
根据@xsc的回答,这些代码是有效的。
我只需要通过提供:form和:env以“正确”的方式在宏定义中调用plus中的宏
(declare my-macro my-plus)
(defn my-plus-in-macro [x y]
(my-macro :form :env (my-plus x y)))
(defmacro my-macro [my-fn]
`~my-fn)
(defn my-plus [x y]
(+ x y))
(my-plus-in-macro 1 2)
3
编辑-0
@xsc您是对的,我不能使用声明转发宏的声明符号。我必须把宏定义放在任何使用它的函数之前
所以,这是正确的代码
(declare my-plus)
(defmacro my-macro [my-fn]
`~my-fn)
(defn my-plus-in-macro [x y]
(my-macro (my-plus x y)))
(defn my-plus [x y]
(+ x y))
(my-plus-in-macro 1 2)
3
宏是在编译时计算的,这意味着编译器/读取器必须知道哪些符号表示宏,哪些符号不表示宏。通过简单地使用
declare
,您并没有提供这些信息,这意味着您将在代码中引用宏函数,而不是将宏调用替换为其计算值
换句话说:当读者第一次遇到my macro
时,除了把它放在那里,没有什么比这更好的了。因为任何宏都只是一个函数(将:macro
元数据设置为true
),所以在下面进一步定义后,这不会造成问题
通过以“正确”的方式调用宏函数,提供通常隐式传递的&form
和&env
参数,可以检查这一事实:
(declare my-macro)
(defn my-function
[x]
(my-macro :form :env x))
(defmacro my-macro
[x]
(vector &form &env x))
(my-function (+ 1 2)) ;; => [:form :env 3]
请注意,表单(+12)
在传递给“宏”之前进行计算,因为我们在这里处理的是一个普通的旧函数
您可以使用前面提到的:macro
元数据将符号声明为宏,但这只会在首次调用时导致异常,因为实际调用是在var绑定到任何对象之前:
(declare ^:macro my-macro)
(defn my-function
[x]
(my-macro x))
;; => IllegalStateException: Attempting to call unbound fn: #'user/my-macro ...
TL;DR:声明宏将不起作用,因为它在读卡器/编译器遇到宏时即被调用,无论它嵌套在函数中有多深。哇!您如何知道宏需要提供:form和:env?有什么阅读建议吗?太糟糕了,官方声明文档中没有关于宏的警告[请注意,
&form
通常包含原始宏调用和执行调用的环境,而不是我上面提供的两个关键字。IIRC,这两个特殊符号在我的第一本Clojure书中提到。我猜它卡住了。:)在编辑中:这不“起作用”从这个意义上说,宏现在正在工作。您只是正确地调用了底层函数,但这意味着使用宏毫无意义。正如我所说的,您不能向前声明宏。