I'm trying to define a macro that should do the following:
I have the following defined in a clojure file:
(defmacro defprop
[prop-name init-value]
`(do
(def ~prop-name ~init-value)
(def ~(symbol (str prop-name "-other")) ~init-value)
(defn ~(symbol (str prop-name "-11")) [] (set! ~prop-name 11))
(defn ~(symbol (str prop-name "-set")) [new-val] (set! ~prop-name new-val))))
When invoked from clojurescript like this (cmacros is the namespace alias):
(cmacros/defprop production 350)
I get the definition of 'production' and 'production-other' right, the function 'production-11' works (and sets to 11 the value of production), but the last one does not work. I get different errors in different browsers. Chrome says "Uncaught SyntaxError: Unexpected token .". Firefox "SyntaxError: missing ) after formal parameters".
The offending javascript code pointed out by Firefox looks like this:
cgamemini.core.production_set = (function
cgamemini$core$production_set(cgamemini.macros.new_val) {
return cgamemini.core.production = cgamemini.macros.new_val;
}
);
Which seems to have all the parenthesis right, although I'm no js specialist. What's going wrong? Why is the -11 definition working but not the -set?
The problem is new-val
. Clojure macro expansion doesn't understand syntax so it doesn't know that new-val
is supposed to be an argument -- any bare symbols that occur in a macro expansion at automatically namespaced to the current namespace. That's why you see cgamemini$core$production_set(cgamemini.macros.new_val)
in your output. What you want to do is use hygienic macro syntax to indicate that a fresh symbol should be created that is not namespaced, e.g.
`(defn ~(symbol (str prop-name "-set")) [new-val#] (set! ~prop-name new-val#))
An alternative is to inject your symbol:
`(defn ~(symbol (str prop-name "-set")) [~'new-val] (set! ~prop-name ~'new-val))