Closed borkdude closed 2 years ago
it'd be beneficial to have macro-expansion happen in same language as runtime so you can take full advantage of power of macros - e.g. using binding
for contextual state
i've run into limitations of this when writing a custom Hiccup compiler / React wrapper. I was trying to compose macros inside a component, each with a bound state by parent macro (defc
), but any unevaluated or quoted expressions would run in JS, so I had to manually eval/macroexpand the desired macro calls (e.g. css
macro for compiling a component's styles)
Macros in Clojure are ran inside the compiler. The JS environment of the compiler doesn't need to be the same environment as the JS target environment (but it can be). The macro function itself isn't visible in the output JS. If you need macros at runtime, this is imo a sign that you just needed to write a function in the first place.
If you need macros at runtime, this is imo a sign that you just needed to write a function in the first place.
Now that I think about it.. I didn't run the logic at runtime since 1/ I wanted the transpiles to take place while compiling, not every time a component was rendered 2/ I did not want transpile logic included in bundle. So you may be right, it wasn't a CLJS-CLJ macro issue but just general dynamic of working with optimized browser code. Sorry for the detour here 😅
So, this is what macros ran by the compiler in JS might look like. macros.cljs
is first compiled to macros.mjs
. Then macros.mjs
is required with :require-macros
from macro_usage.cljs
. The compiler loads the (already compiled) macro code before it traverses the whole file, so it has the macros available for execution.
Possible later convention for inline macros / requiring macros:
By convention we could have
foo.mjs
foo$macros.mjs
and when you (:require ["./foo.mjs" :refer [bar]) then foo$macros
would be loaded in the compiler and there would be a check if bar is macro.
Likewise, inline macros would be emitted to foo$macros.mjs
instead of foo.mjs
Part 1 is now complete with the transpiler approach. See examples/react.
Macros need to run in the same environment as the transpiler. Therefore it makes sense to force users to declare macros in separate
.cljc
files:.cljc
so the cherry transpiler can run in both clojure/bb and JS environments.Say we have
foo.cljs
which compiles tofoo.mjs
but requires a macro frombar.cljc
, how would we declare that?Or we can make the choice that macros are also transpiled functions and cherry only runs in JS. Then you could even write macros in JavaScript... I'm actually leaning towards that now.
Similar to what bun.js does: https://twitter.com/jarredsumner/status/1493204320984047619
We should explore both options.
Interpreter approach
The interpreter approach needs you to hold deps on the "classpath" when you want to use macros from another library, since macros would be elided in .mjs output.
Transpiled macros approach
...