我可以在 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
和朋友的简单辅助函数,因此您可以在宏编写过程中一次一步地看到发生了什么。享受吧!
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
和朋友的简单辅助函数,因此您可以在宏编写过程中一次一步地看到发生了什么。享受吧!