Marcus Junius Brutus
Marcus Junius Brutus

Reputation: 27306

function that creates macro with macro name supplied as a parameter

I am trying to define a function that will create a macro and am running into problems when I try to dynamically supply the macro's name. Here's narrowed-down code that exemplifies the issue I am facing:

(defn create-times-macro [n]
  (defmacro thatManyTimes [a]
    `(* ~n ~a)))

(create-times-macro 2)

(thatManyTimes 3) ;; evals to 6

So far so good. Now say I want to supply the macro's name as a parameter:

(defn create-times-macro [macroName n]
  (defmacro macroName [a]
    `(* ~n ~a)))

(create-times-macro (symbol "multiplyBy") 3)
(multiplyBy 3) ;; fails with unable to resolve symbol multiplyBy
(create-times-macro "multiplyBy" 3)
(multiplyBy 3) ;; same failure

Upvotes: 1

Views: 100

Answers (3)

Ankur
Ankur

Reputation: 33657

It seems that you are confused about the use of macros.

  • A macro which create a function - OK
  • A macro which create another macro - OK
  • A function creating a macro - Not OK (not common and usual).

All these points suggest the fact that macros are compile time thing where as functions are run-time thing. The usual direction is compile time to run-time where as the last point is about going from runtime to compile time.

Your first code example works in REPL but it won't work in a compiled JAR. In REPL you are in a compile-time->run-time->print->loop and becoz of the loop you sort of go back to compile->time from run-time and that is why the last point work in REPL. In compiled code you will only have, well, compiled code and there is only runtime world, unless you use eval in your code which can bring you back to compile time.... Wait my head hurts but I hope this make things clear :)

Upvotes: 4

mobyte
mobyte

Reputation: 3752

You don't need macro in this case. Functions are suitable in most cases and they have more abilities than macros. For example, if you create it as function

(defn create-times-fn [n]
  (fn [a]
    (* n a)))

you would get the same result

(def three-times (create-times-fn 3))

(three-times 2)
=> 6

or

(let [three-times (create-times-fn 3)]
  (three-times 2))

And you can pass it as argument to other functions

(map (create-times-fn 2) (range 5))
=> (0 2 4 6 8)

or return it as result or compose it with other functions. You lose all these things with macros.

Upvotes: 1

OpenSauce
OpenSauce

Reputation: 8633

Not sure if this is the best way to do it, but it works:

=> (defmacro create-times-macro [macroName n]
     (let [the-name (symbol macroName)]
       `(defmacro ~the-name [a#]
          (* ~n a#))))
#'user/create-times-macro
=> (create-times-macro "hi" 3)
#'user/hi
=> (hi 4)
12

Upvotes: 1

Related Questions