帮我写一个Clojure宏,它自动将元数据添加到函数定义中

时间:2009-06-12 22:34:02

标签: lisp clojure macros defn

我意识到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包中看到了一些看起来可能有用的东西,但很难找到使用它们的示例代码。

1 个答案:

答案 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是一种特殊形式,与语言的其他部分相比,它处理的元数据有点奇怪。它将vardef的元数据设置为命名var的符号的元数据;我认为这是上述工作的唯一原因。如果您想查看所有内容的内容,请参阅Clojure源代码DefExpr中的Compiler.java课程。

最后,Programming Clojure的第216页说:

  

您通常应该避免宏扩展中的读取器宏,因为在宏扩展开始之前,读取器宏是在读取时进行评估的。