ClojureScript Macros Calling Functions
Let's say you are writing a complex macro and decide to delegate some of its implementation to a function. When in bootstrapped ClojureScript, this can be fairly clean to do.
But first, let's back up and review how this can be done in regular ClojureScript—where macros are written in Clojure.
(ns foo.macros) (defn add* [a b] (+ a b)) (defmacro add-now [a b] (add* a b))
add-now macro delegates to an
add* function defined in the same namespace. This will work fine if the values passed for
b are compile-time constants. In a REPL, you can
(foo.macros/add-now 1 2)
3. What's really going here is Clojure produced the
3 at macroexpansion time. But you can't do
(let [x 3 y 4] (foo.macros/add-now x y))
If you try it you'll see that it ends up trying to add the symbols
y, not their values.
But, you can instead have a macro that expands to a call to a ClojureScript function. Let's say you add this to the
(defmacro add [a b] `(foo.core/add* ~a ~b))
src/foo/core.cljs that defines a ClojureScript version of
(ns foo.core) (defn add* [a b] (+ a b))
Now if you
(require-macros 'foo.macros :reload)
then this works fine:
(let [x 3 y 4] (foo.macros/add x y))
In regular ClojureScript, we need to play this game because of the use of Clojure for macros.
In bootstrapped ClojureScript, on the other hand, everything is pure ClojureScript and the following approach works just fine:
(ns foo.macros) (defn add* [a b] (+ a b)) (defmacro add-now [a b] (add* a b)) (defmacro add [a b] `(add* ~a ~b))
The only real difference is that in the
add macro, we are no longer referring to
foo.core/add*, but can simply use the existing
add* function definition in the
With this in place, the
add macro works just fine.
For the above to work as written, you'll need
tools.reader1.0.0-alpha3 or later for a syntax-quote behavior fix (TRDR-33).
If you are interested in digging a little deeper into what is going on here, do
(macroexpand '(foo.macros/add 3 4))
and look at what you get:
(foo.macros$macros/add* 3 4)
This has to do with the fact that macros namespaces are compiled in a different stage, and end up in a pseudo-namespace involving a
$macros suffix. (This implementation detail was exploited in a previous post.) In fact, an unfortunate consequence is that you cannot refer to
foo.macros/add*. You must instead let the compiler take care of var resolution for you (either directly or via syntax-quote).