Reputation: 13796
Clojure macro noob here. I have a function with some optional parameters, e.g.
(defn mk-foo [name & opt]
(vec (list* name opt)))
giving this:
user> (mk-foo "bar" 1 2 3)
["bar" 1 2 3]
I'm trying to write a macro which takes the same optional arguments and passes them transparently to an invocation of mk-foo
. So far I have this:
(defmacro deffoo [name & opt]
`(def ~name ~(apply mk-foo (str name) opt)))
which has the desired effect:
user> (macroexpand '(deffoo bar 1 2 3))
(def bar ["bar" 1 2 3])
The use of apply
to flatten the list opt
feels clumsy. Is there an idiomatic way to do this? I'm guessing ~@
is needed, but I can't get the quoting right. Many thanks.
Upvotes: 3
Views: 636
Reputation: 91554
Your intuition about using apply served you well in this case. When you have a quoted form ` and then unqote all of them it can help to think about moving the un-quoting down to the smallest part or the list. This avoids using code to generate forms that could be simply written.
user=> (defmacro deffoo [name & opt] `(def ~name [~(str name) ~@opt]))
#'user/deffoo
user=> (macroexpand '(deffoo "bar" 1 2 3))
(def "bar" ["bar" 1 2 3])
and here it is with the call to mk-foo:
(defmacro deffoo [name & opt] `(def ~name (mk-foo ~(str name) ~@opt)))
#'user/deffoo
user=> (macroexpand '(deffoo "bar" 1 2 3))
(def "bar" (user/mk-foo "bar" 1 2 3))
in this second case we move the ~
in one level and let the call to mk-foo stay quoted and only unquote the args required to build the parameter list (using splicing-unquote as you suspected)
Upvotes: 3