Search code examples
macrosclojurequil

Clojure macro inside another macro: how to generate string from symbol


Probably the title is not 100% correct, but let me show you the issue:

(defmacro nvp!
  [n width height]
  `(q/defsketch (symbol (str (name '~n) "-viewport" ))
                  :title (name '~n))

In short: there's a macro called defsketch (it's part of the lib quil, but that does not matter). It's signature is basically defsketch [applet-name & options], and it creates something and binds it to a var called applet-name. For some reason, I want to wrap this into another macro that -- among other things -- takes an applet-name parameter, extends that name into applet-name-viewport and passes that to defsketch. I, however, am unable to figure out how to do that correctly (using macroexpand on the code above ((pprint (macroexpand(nvp test-name 500 500))), I get

(def(clojure.core/symbol (clojure.core/str (clojure.core/name 'my-namespace.core/test-name) "-viewport"))
 (quil.applet/applet
 :title (clojure.core/name 'my-namespace.core/test-name)))

(clojure.core/symbol (clojure.core/str (clojure.core/name 'my-namespace.core/test-name) "-viewport") -- this part looks good, but it should be evaluated somehow before passing it to the inner macro...


Solution

  • You need to unquote the form that generates the new symbol instead of the original symbol itself.

    Here is a small example of how to accomplish this using a defn like macro for the inner macro:

    (defmacro mydefn
      [name & body]
      `(defn ~name ~@body))
    
    (defmacro defnview
      [n & body]
      `(mydefn ~(symbol (str (name n) "-viewport")) ~@body))
    
    ;; OR
    
    (defmacro defnview
      [n & body]
      (let [n (symbol (str (name n) "-viewport"))]
        `(mydefn ~n ~@body)))
    

    Example:

    (defnview foo [a] a)
    ;; => #'user/foo-viewport
    
    (foo-viewport 1)
    ;; => 1
    
    (macroexpand '(defnview foo [a] a))
    ;; => (def foo-viewport (clojure.core/fn ([a] a)))