In Clojure, how do I make a library macro which processes supplied functions metadata and return some result? Amount of functions is unlimited and they should be passed without being boxed into a sequence ((my-macro fn1 fn2)
instead of (my-macro [fn1 fn2])
)
Say, we expect function vars having :foo keys in meta and the macro concatenates their values. The following snippet should work in REPL (considering my-macro
is in the namespace):
user=> (defn my-func-1 {:foo "bar"} [])
(defn my-func-1 {:foo "bar"} [])
#'user/my-func-1
user=> (defn my-func-2 {:foo "baz"} [])
(defn my-func-2 {:foo "baz"} [])
#'user/my-func-2
user=> (my-macro my-func-1 my-func2)
(my-macro my-func-1 my-func2)
"barbaz"
I tried several approaches but was only able to process single function so far.
Thanks!
Try this:
How it Works
If you expand the macro you can start to see the parts in play.
Function metadata is stored on the var, so using
(meta my-func-1)
is not sufficient. But,var
is a special form and does not compose like a normal function.This anonymous function exists inside an escaped form, so it is processed inside the macro to produce the output forms. Internally it will create a the
(:foo (meta (var my-func-1)))
form by first backtick escaping the outer form to declare it a literal, and not evaluated, list and then unescaping thex
var with a tilde to output the value instead of the symbol.This entire form is backtick escaped, so it will be returned literally. But, I still need to evaluate the map function generating the
(:foo (meta (var my-func-1)))
form. In this case I have unescaped, and spliced (@
) the result of, themap
form directly. This first evaluates the map function and returns a list of generated forms, and then takes the contents of that list and splices it into the parent list.You could also split out the
map
function in alet
statement before hand for slightly more readability.