2

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
  • This question is different than the referenced "duplicate". I edited to show the difference. – Colin Steele May 10 '17 at 19:53
  • You have run into a big limitation of macros: a macro cannot be used as an argument go another function. Please see "Macros All the Way Down" at http://www.braveclojure.com/writing-macros/ – Alan Thompson May 10 '17 at 23:51
  • Thanks Alan. The approach in https://stackoverflow.com/questions/43904628/how-to-create-clojure-defn-functions-automatically/43904717#43904717 will not work because clojurescript doesn't have `intern`. – Colin Steele May 11 '17 at 15:08
  • I re-worked the answer to make it clearer (hopefully) and added the all-macro solution: http://stackoverflow.com/questions/43958471/how-to-create-clojure-defn-functions-automatically-without-macros The other alternative (#3) is to just make a new (anonymous) wrapper fn and register it each time. May be the easiest solution in CLJS – Alan Thompson May 13 '17 at 22:22

0 Answers0