This was a great example, and I think you are absolutely right that it
is the identical in function, if not in form, to mine.
Interestingly, in the course of concocting a counter-counter example,
I came up with this in an effort to show the two dynamic variables we
stipulate - host and target - were absolutely required:
#+clj
(defmacro my-macro []
(cond
(contains? *features* :clj) `(some-clojure-thing)
(contains? *features* :cljs) `~(do (require 'some-ns)
((resolve 'some-ns/some-fn)))))
My hope was to make the point that 'some-ns/some-fn would run in a
context where (
features :cljs) is true - as it is in the caller's
(macro body) environment - which would result in the system attempting
to run ClojureScript code, which would explode because the macro is
Clojure. Then I realized that require runs load, and load is in the
macro body. The load/require available in the environment is
Clojure-specific! Thus, load/require can bind disj :cljs and conj
:clj before reading and compiling some-ns.
My counter-example failed, but did bring me a little closer to the
truth - that we only need one dynamic variable, platform. Any
platform that has the ability to load code also has an opportunity to
bind platform and thereby inform macros of target. This simplifies
the Feature Macro proposal by half and we are excited to amend it.
Our updated cross-platform example becomes:
(case-platform
:cljs nil
:clj (defmacro my-macro []
(condp = *platform*
:clj `(some-clojure-thing)
:cljs `(some-cljs-thing))))
Instead of
host and
target, we are down to just
platform. We
continue to see no need for a
features set because of Clojure's
platform-symbiosis.