我可以在 Clojure 的宏中引用宏吗?

Can I reference a macro from within a macro in Clojure?

Clojure。草书。回复。 Windows 10. 都是最新的。

这是我的 'base' 宏:

(defmacro PutProp! [Sym Val PName]
  `(reset-meta! (var ~Sym) (assoc (meta (var ~Sym)) ~PName ~Val)))

我不知道这在做我想做的事情时是否正确,即修改 Sym 的元数据键名 PName 与新的 Val 值。我是这方面的新手,我确定我已经跳进了深水池。我正在使用宏,因为我认为它可以避免我在 Clojure 中不理解的混乱的变量绑定问题。

然后我写了另一个宏来在一次调用中定义很多元数据:

(defmacro DefMeta! [L]  ;; Given a symbol (first L), define metadata for it given in (next L).
  `(let [S (first ~@L)]     ;; The symbol we're modifying is the first element in list L.
     (map (fn [[K V]] (PutProp! S V K)) (next ~@L))))   ;; (next L) is the list of key/value pairs.

我也不确定这是不是我想写的。 在 REPL 工作,我定义了一个符号:

(def Sym 0)

然后我做一个DefMeta的macroexpand-1!:

(macroexpand-1 '(DefMeta! [Sym :VName :VValue]))

我得到以下信息:

(clojure.core/let
 [thic.core/S (clojure.core/first Sym :VName :VValue)]
 (clojure.core/map
  (clojure.core/fn [[thic.core/K thic.core/V]] (thic.core/PutProp! thic.core/S thic.core/V thic.core/K))
  (clojure.core/next Sym :VName :VValue)))

这不会宏扩展 PutProp!宏.

我很难过。我有 4 本关于 Clojure 编程的书籍,其中 none 提到了宏调用中的宏调用。它甚至是合法的吗?如果是这样,我如何完全展开内部宏,以便我可以查看我所写的是否是我想要的? [...常年存在的程序员问题...]

PS 我尝试了 macroexpand-all,但我对一切的宏扩展感到不知所措,甚至是 Clojure 核心函数。
比这少的东西,请。请?

which doesn't macro-expand the PutProp! macro.

是的,macroexpand-1 只执行一次宏展开。宏展开的整个过程是一个不动点计算,每棵树都会展开,直到达到无法再展开的形式。

您可能想要使用 macroexpand-all

此外,您的宏中的输入列表是一个表达式列表,您希望在宏展开时获取此列表的第一个和下一个。

(first ~@L)

以上展开为:

(clojure.core/first Sym :VName :VValue)

您实际上只想要 Sym,即:

~(first L)

一个宏的定义使用另一个宏是完全合法的(而且很常见)。

您可以看到一个关于恕我直言逐步构建宏的最佳方法的示例

我不喜欢使用 macroexpand-* 函数。上面 link 中的答案使用 println 和朋友的简单辅助函数,因此您可以在宏编写过程中一次一步地看到发生了什么。享受吧!