Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass optional macro args to a function

Tags:

clojure

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.

like image 376
fizzer Avatar asked Dec 14 '25 16:12

fizzer


1 Answers

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)

like image 50
Arthur Ulfeldt Avatar answered Dec 18 '25 05:12

Arthur Ulfeldt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!