【发布时间】:2021-07-13 22:08:14
【问题描述】:
我可以用宏“生成”一个 def。
(defmacro my-def [my-name]
`(def ~my-name 42))
(my-def a)
a; => 42
如果我尝试对列表做类似的事情
(defmacro my-defs [my-names]
`(do
~@(for [name# my-names]
`(def ~name# 42))))
(my-defs (a b c))
(macroexpand '(my-defs (a b c))); => (do (def a 42) (def b 42) (def c 42))
只要我使用文字列表作为输入,它就可以工作。但是只要我想传入一个var
(def my-list '(a b c))
(macroexpand '(my-defs my-list)); => Don't know how to create ISeq from: clojure.lang.Symbol
我很难获得my-names 的值。我不能使用 ~my-names,因为它已经在取消引用拼接 (~@) 中使用,并且会导致“尝试 [...] 调用未绑定的 fn”。
我错过了什么?
我需要使用(var-get (resolve my-names))吗?
在这些情况下,宏是否需要“检测”传递的参数是文字值还是 var 并相应地采取行动以便对两者都起作用?
或者使用eval 来避免这种情况是惯用的吗?
解决@Alan Thompson 的问题“[...] 你为什么要这样做?”:我有一个“资源”的规范(深度嵌套的映射),拥有一个宏会相当方便为这些资源生成 defs(记录),以便以后使用它们。所以我想没有什么不寻常的“它会干掉事情”的原因。 :) 此时我找到了一种方法,将my-names 包装在eval 中。剩下的问题是:这是惯用的,还是有更好的方法?
【问题讨论】:
-
你能解释一下你为什么要这样做吗?它确实有助于找到合适的答案。如果你想坚持只写一个宏来创建一堆
def语句,请看这个答案:stackoverflow.com/questions/60212576/… -
感谢您的回答。我添加了一段来解决您的有效问题。我会研究你链接的答案。
-
@branch14 回答你为什么要这样做:你说你有一个深度嵌套的资源地图。例如,我将使用 postwalk 遍历此地图并构建一个从关键字到资源的大地图(而不是为每个资源生成一个 def):
(def resources (postwalk ...))。然后,我只需通过此映射中的键访问资源:(:my-resource resources)(而不是拥有deffed 资源my-resource)。 -
@Rulle 谢谢你的建议。这完全是我在其他情况下会选择(并且已经这样做)的路线。但在这种情况下,从 API 的角度来看,它并不能解决问题。
-
@branch14 我明白了。如果您包含深度嵌套的资源映射的一部分以及应该如何从该映射生成定义的一些示例,那么您很可能会得到一个更好地解决您的问题的答案。
标签: clojure