8

I have two macros. The first one takes a symbol as the only parameter (because it's passed to def, which needs a symbol). The second function takes a list of symbols and should call the first with each symbol individually.

(defmacro m1 [s]
  '(let [f# ... dynamic function definition ...]
      (def ~s f#))

The second macro should take a list of symbols and pass them to the first, but I can't get it to work. The best I could come up with was the following:

(defmacro m2 [symbols]
   `(for [s# ~symbols] (eval (read-string (str "(name.of.current.namespace/m1 " s# ")")))))

which forces the s# to be evaluated before it is passed to the first macro. It is also invoked with a list of strings, rather than a list of symbols.

This is useful for a library which I am using for which all of the functions in the library takes the same two first parameters. I am trying to create wrapper functions, in my namespace, for some of the functions, which automatically provides the first two parameter values which are common to all of them.

Any ideas to improve this?

2
  • 1
    Why a plain fn is not an option here? Something along the lines of (defn m2fun [symbols] (map m1 symbols)) Commented Mar 10, 2011 at 13:43
  • Can you just use partial? I don't really getting what the problem is. Commented Mar 10, 2011 at 15:10

1 Answer 1

8

Usually when you ask how to get two macros to cooperate, the answer is to not make them both macros. I think my blog post on macro-writing macros will help clarify. For this particular situation I'd probably combine two suggestions from the comments:

(defmacro build-simpler-functions [& names]
  (cons 'do
        (for [name names]
          `(def ~(symbol (str "simple-" name))
             (partial ~name 5 10))))) ; if you always pass 5 and 10

(build-simpler-functions f1 f2)

This expands to

(do
  (def simple-f1 (clojure.core/partial f1 5 10))
  (def simple-f2 (clojure.core/partial f2 5 10)))

which looks like basically what you want.

Edit: if the args you "always" pass are different for each function, you can do this:

(defmacro build-simpler-functions [& names]
  (cons 'do
        (for [[name & args] names]
          `(def ~(symbol (str "simple-" name))
             (partial ~name ~@args)))))

(build-simpler-functions [f1 5 10] [f2 "Yo dawg"]) ; expansion similar to the previous
Sign up to request clarification or add additional context in comments.

2 Comments

This is spot-on: The (cons 'do ...) and ~(symbol s#...) did the thing. The mind-screw was getting around the idea of which code is being executed and which code is being returned and how it influences each other.
You might also like clojure.contrib.macro-utils.macrolet for one-off macros like this. I don't like polluting my namespaces with macros I only use once, so you could write (macrolet [(defpartial [& funcs] (cons 'do ...))] (defpartial [f1 5 10] [f2 'omg 'more 'args]))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.