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

时间:2021-01-21 14:20:22

标签: clojure macros

Clojure。草书。复制。 Windows 10。全部是最新的。

这是我的“基本”宏:

(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!宏。

我难住了。我有四本关于 Clojure 编程的书,没有 提到宏调用中的宏调用。它甚至合法吗?如果是这样,我如何完全扩展内部宏,以便我可以查看我写的内容是否是我想要的? [...程序员长期存在的问题...]

PS 我尝试了 macroexpand-all 并且我对一切的宏扩展感到不知所措,甚至是 Clojure 核心功能。
请少一点。请?

2 个答案:

答案 0 :(得分:3)

<块引用>

它不会宏扩展 PutProp!宏。

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

您可能想要使用 macroexpand-all

另外,你的宏中的输入列表是一个表达式列表,你想在宏展开时取这个列表的第一个和下一个。

(first ~@L)

以上扩展为:

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

您实际上只想要Sym,即:

~(first L)

答案 1 :(得分:2)

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

您可以看到一个示例,说明逐步构建宏的最佳方式(恕我直言)in this question

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