使用前向声明的宏时,args的数量错误

mav*_*ozo 4 clojure

我有一个函数my-plus-in-macro,它包含一个名为my-macro的宏.但my-macro是在my-plus-macro定义之后声明的.我使用declare来避免无法解决的符号错误.

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

然后,如果执行此操作,我希望得到3

(my-plus-in-macro 1 2)
Run Code Online (Sandbox Code Playgroud)

但我得到了,

ArityException Wrong number of args (1) passed to: user$my-macro  clojure.lang.AFn.throwArity (AFn.java:437)
Run Code Online (Sandbox Code Playgroud)

但是,如果我在repl中再次执行此my-plus-in-macro定义

(defn my-plus-in-macro [x y]
    (my-macro (my-plus x y)))
Run Code Online (Sandbox Code Playgroud)

然后我执行这个(my-plus-in-macro 1 2)

正是我所期待的,3

我第一次执行(my-plus-in-macro 1 2)会发生什么?

不知怎的,当我确定我加在宏首次,Clojure中看到我的宏符号,但它不知道的还以为我打算用它作为一个宏的名称.

虽然我在定义my-plus-in-macro后立即将my-macro定义为符号,但是clojure仍然不知道my-macro的符号.这就是我得到ArityException的原因.

但是,不知何故,当我重新定义了我的加在宏代码相同,Clojure的现在知道我的宏是一个宏,将其视为这样,然后我的代码工作.

当然,如果我将my-macro定义放在my-plus-in-macro定义之前,不会得到任何异常.但这是否意味着我不能使用declare为宏保留一个符号?

编辑

根据@xsc的回答,这些代码有效.我只需要通过提供:form和:env,使用"正确"方式在my-plus-in-macro定义中调用my-macro

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

编辑-0

@xsc你是对的,我不能使用declare来声明宏的前向声明符号.我必须在任何使用它的函数之前放置宏定义.

所以,这是正确的代码

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

xsc*_*xsc 9

宏在编译时进行评估,这意味着编译器/阅读器必须知道哪些符号代表宏,哪些符号代表宏.通过简单地使用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]
Run Code Online (Sandbox Code Playgroud)

请注意,表单(+ 1 2)在传递给"宏"之前会被评估,因为我们在这里处理一个普通的函数.

您可以使用上述:macro元数据将符号声明为宏,但这只会在首次调用时导致异常,因为实际调用是在var绑定到任何内容之前:

(declare ^:macro my-macro)

(defn my-function
  [x]
  (my-macro x))
;; => IllegalStateException: Attempting to call unbound fn: #'user/my-macro ...
Run Code Online (Sandbox Code Playgroud)

TL; DR:声明一个宏将无法工作,因为无论它在函数内部嵌套的程度如何,它都会在读取器/编译器遇到它时被调用.