19

Macro Clubの最初のルールはマクロを使用しないことであると理解しているので、次の質問は、他の何よりもClojureを学習するための演習として意図されています(これは必ずしもマクロの最良の使用ではないことを理解しています)。

(defn)通常のマクロのラッパーとして機能し、定義された関数にメタデータを追加する単純なマクロを作成したいと思います。だから私はこのようなものが欲しいです:

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

...次のようなものに拡張します:

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

原則として、これは私にはそれほど難しいことではないように思われます[args]が、定義された関数のおよびその他のフォームを正しく解析するための詳細を特定するのに苦労しています。

ボーナスとして、可能であれば、マクロがdefnのすべての異なる形式(つまり、docstring、複数のアリティ定義などの有無にかかわらず)を処理できるようにしたいと思います。clojure-contrib/defパッケージには役立つと思われるものがいくつかありましたが、それらを使用したサンプルコードを見つけるのは困難でした。

4

1 に答える 1

18

Updated:

The previous version of my answer was not very robust. This seems like a simpler and more proper way of doing it, stolen from 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}

#^{} and with-meta are not the same thing. For an explanation of the difference between them, see Rich's discussion on the Clojure mailing list. It's all a bit confusing and it's come up a bunch of times on the mailing list; see also here for example.

Note that def is a special form and it handles metadata a bit oddly compared with some other parts of the language. It sets the metadata of the var you're deffing to the metadata of the symbol that names the var; that's the only reason the above works, I think. See the DefExpr class in Compiler.java in the Clojure source if you want to see the guts of it all.

Finally, page 216 of Programming Clojure says:

You should generally avoid reader macros in macro expansions, since reader macros are evaluated at read time, before macro expansion begins.

于 2009-06-12T23:20:42.210 に答える