views:

67

answers:

2

I would like to have a macro which I'll call def-foo. Def-foo will create a function, and then will add this function to a set.

So I could call

(def-foo bar ...)

(def-foo baz ...)

And then there would be some set, e.g. all-foos, which I could call:

all-foos
=> #{bar, baz}

Essentially, I'm just trying to avoid repeating myself. I could of course define the functions in the normal way, (defn bar ...) and then write the set manually.

A better alternative, and simpler than the macro idea, would be to do:

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

But I'm still curious as to whether there is a good way for the macro idea to work.

+5  A: 

Have the macro add the name of the new function to your set before creating the function, like so:

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

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

Result:

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}>
Greg Harman
+5  A: 

Do you want to end up with a set that has the names of functions in it (i.e. a set of Symbols), or a set containing vars (which resolve to functions)? If you want the former, you can add the symbols to an atom in the macro at compile-time, like Greg Harman's version.

If you want the latter, your macro must expand to code that does the atom-swapping after the function is defined. Remember that macros run at compile-time and the macro-expanded result runs at run-time; the function itself is not available until run-time.

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

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

If you want to be able to call the functions in this set, for example, you need to use the latter version.

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