我可以创建一个clojure宏,它将允许我获取宏创建的所有函数的列表吗?

时间:2010-06-01 21:11:06

标签: macros clojure dry

我想要一个我称之为def-foo的宏。 Def-foo将创建一个函数,然后将此函数添加到集合中。

所以我可以打电话给

(def-foo bar ...)

(def-foo baz ...)

然后会有一些设置,例如all-foos,我可以称之为:

all-foos
=> #{bar, baz}

基本上,我只是想避免重复自己。我当然可以用正常方式定义函数,(defn bar ...)然后手动编写集合。

一个更好的选择,比宏观思想更简单,就是:

(def foos #{(defn bar ...)    (defn baz ...)} )

但我仍然很好奇是否有一个很好的方式让宏观思想发挥作用。

2 个答案:

答案 0 :(得分:5)

让宏在创建函数之前将新函数的名称添加到集合中,如下所示:

(def *foos* (atom (hash-set)))

(defmacro def-foo [name]
  (swap! *foos* conj name)
  `(defn ~name
     []
     (println "This is a foo!")))

结果:

user=> (def-foo bar)
#'user/bar
user=> (def-foo baz)
#'user/baz
user=> (bar)
This is a foo!
nil
user=> (baz)
This is a foo!
nil
user=> *foos*
#<Atom@186b11c: #{baz bar}>

答案 1 :(得分:5)

你想最终得到一个具有其中的函数名称的集合(即一组符号),或者包含变量的集合(解析为函数)?如果你想要前者,可以在编译时将符号添加到宏中的原子,就像Greg Harman的版本一样。

如果你想要后者,你的宏必须扩展到定义函数后进行原子交换的代码。请记住,宏在编译时运行,宏扩展结果在运行时运行;函数本身在运行时才可用。

(def all-foos (atom #{}))

(defmacro def-foo [x]
  `(let [var# (defn ~x [] (println "I am" '~x))]
     (swap! all-foos conj var#)))

例如,如果您希望能够调用此集合中的函数,则需要使用后一版本。

user> (def-foo foo)
#{#'user/foo}
user> (def-foo bar)
#{#'user/foo #'user/bar}
user> ((first @all-foos))
I am foo
nil