19

我意识到 Macro Club 的第一条规则是不要使用宏,所以下面的问题更多地是作为学习 Clojure 的练习而不是其他任何东西(我意识到这不一定是宏的最佳使用)。

我想编写一个简单的宏,它充当常规(defn)宏的包装器,并最终将一些元数据添加到定义的函数中。所以我想要这样的东西:

(defn-plus f [x] (inc x))

...扩展成这样的东西:

(defn #^{:special-metadata :fixed-value} f [x] (inc x))

原则上这对我来说似乎并不难,但我无法[args]确定正确解析定义函数中的和其他形式的细节。

作为奖励,如果可能的话,我希望宏能够处理所有不同形式的 defn(即,有或没有文档字符串、多个 arity 定义等)。我在clojure-contrib/def包中看到了一些看起来可能有用的东西,但是很难找到使用它们的示例代码。

4

1 回答 1

18

更新:

我的答案的先前版本不是很可靠。这似乎是一种更简单,更正确的方法,从以下位置窃取clojure.contrib.def

(defmacro defn-plus [名称和符号]
  `(defn ~(vary-meta name assoc :some-key :some-value) ~@syms))

user> (defn-plus ^Integer f "Docstring 到这里" [x] (inc x))
#'用户/f
用户>(元#'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.整数}

#^{}with-meta不是一回事。有关它们之间差异的解释,请参阅 Rich 关于Clojure 邮件列表的讨论。这有点令人困惑,它在邮件列表中出现了很多次;例如,另请参见此处

请注意,这def是一种特殊形式,与语言的其他部分相比,它处理元数据有点奇怪。它将var您要def查找的元数据设置为命名 var 的符号的元数据;我认为这是上述工作的唯一原因。如果您想了解所有内容,请参阅 Clojure 源代码中的DefExpr类。Compiler.java

最后,Programming Clojure的第 216 页说:

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

于 2009-06-12T23:20:42.210 回答