我意识到Macro Club的第一条规则是不要使用宏,所以下面的问题更像是学习Clojure的练习而不是其他任何东西(我意识到这不一定是最好的使用< / em> of macros)。
我想编写一个简单的宏,它充当常规(defn)
宏的包装器,并最终向定义的函数添加一些元数据。所以我想要这样的东西:
(defn-plus f [x] (inc x))
......扩展到类似的东西:
(defn #^{:special-metadata :fixed-value} f [x] (inc x))
原则上这对我来说似乎并不难,但是我无法确定正确解析定义函数中[args]
和其他形式的具体细节。
作为奖励,如果可能的话,我希望宏能够处理所有不同形式的defn(即,有或没有docstrings,多个arity定义等)。我在clojure-contrib/def
包中看到了一些看起来可能有用的东西,但很难找到使用它们的示例代码。
答案 0 :(得分:18)
<强>更新强>
我的答案的先前版本不是很强大。这似乎是一种更简单,更正确的方法,从clojure.contrib.def
:
(defmacro defn-plus [name & syms] `(defn ~(vary-meta name assoc :some-key :some-value) ~@syms)) user> (defn-plus ^Integer f "Docstring goes here" [x] (inc x)) #'user/f user> (meta #'f) {:ns #<Namespace user>, :name f, :file "NO_SOURCE_PATH", :line 1, :arglists ([x]), :doc "Docstring goes here", :some-key :some-value, :tag java.lang.Integer}
#^{}
和with-meta
不是一回事。有关它们之间差异的解释,请参阅Rich对Clojure mailing list的讨论。这有点令人困惑,它在邮件列表上出现了很多次;另请参阅here。
请注意,def
是一种特殊形式,与语言的其他部分相比,它处理的元数据有点奇怪。它将var
你def
的元数据设置为命名var的符号的元数据;我认为这是上述工作的唯一原因。如果您想查看所有内容的内容,请参阅Clojure源代码DefExpr
中的Compiler.java
课程。
最后,Programming Clojure的第216页说:
您通常应该避免宏扩展中的读取器宏,因为在宏扩展开始之前,读取器宏是在读取时进行评估的。