I don't write macros particularly often. I have never been in this situation, where I would like to defn
a number of functions based on information known at compile time, from a vector. The use case is creating a wrapper lib in clojurescript for a JS library, where there are a number of methods on a JS object which I'd like to wrap with more idiomatic cljs functions.
Unlike clojure macro to generate functions this question hinges on the macro being called with a non-literal argument, which is passed into the macro as a form (not evaluated), which must then be evaluated before the macro can return a code form.
Below, the individual call to defevent
works as expected, creating a function in the core
ns for on-x
. However, when called in the map
, operating on events
, a vector of keywords, it fails with:
WARNING: Use of undeclared Var foo.core/defevent at line 1 <cljs repl>
#object[TypeError TypeError: Cannot read property 'cljs$core$IFn$_invoke$arity$1' of undefined
I suspect this is because macros receive arguments as unevaluated forms, and somewhere in the map
source, my defevent
is called with a form more-or-less like (defevent (rest foo))
or somesuch... So how would I rewrite defevent
, or the the (map defevent events)
code to enable defn
'ing functions based on the values in events
?
macros.clj:
(ns foo.macros)
(defmacro defevent
[event-kw]
`(defn ~(symbol (str "on-" (name event-kw)))
[~'this ~'keyvec ~'cb]
(.call (aget ~'this ~(name event-kw))
~'this
~'keyvec
~'cb)))
core.cljs:
(ns foo.core
(:require-macros [foo.macros :refer [defevent]])
(defevent :x) ;; works
(def events [:a :b :c])
(doall (map defevent events)) ;; fails