are there any resources on how to write macros for clojurescript? i have a macro that's working great in clojure and i'm trying to make it work in clojurescript, and i feel like i'm not making any progress. it seems like because macroexpansion happens in jvm clojure, reader conditionals in functions used by macros are taking the :clj branch, not the :cljs branch. if that's the case, how do i write macros that do what i need?
okay, i read @thhellerβs blog post. his recommendation to not use cljc is intriguing, but i'm not sure how that works when i'm trying to produce cross-dialect macros
or will writing :require-macros in an otherwise empty file mean that users can access it?
The require-macros bit is required only at the caller end in CLJS only.
For CLJC code, which I think is what your use case is, it's convenient to put defmacro and require-macros both in the same .cljc file so that it's cleaner for the caller, but you need to be aware of how things are playing out under the hood.
right, it seems that relying on reader conditionals doesn't work when writing complex macros, which feels like a pretty big footgun
i really wish clojurescript was written in javascript, not clojure...
Macros are expanded at compile time, in CLJ (also for CLJS) - that's the first step in understanding the lifecycle.
yes, that's what i'm objecting to. macros are not expanded in CLJ when you use #basilisp or #glojure or phel
only in clojurescript do we have to play this awkward dance of "which environment does this exist in?"
ideally, a cljc file is read in by the host, using the appropriate reader conditional, and then each form is evaluated top to bottom in that host language's environment
but clojurescript doesn't work like that
I think i'll just not support clojurescript in my projects, I use too many macros and see no way around this mess
thanks for the input, everyone
FWIW the reason I recommend splitting the macros and using clj+cljs files instead of cljc is simply because it makes the distinction clearer of what runs where. of course that is only really possible when targeting CLJS only. writing macros that need to work in CLJ and CLJS environments do have to use cljc. understanding the finer points of the split in that context is a bit trickier since you are basically writing two files in one at the same time.
makes me think using a cljs-specific macros namespace is the best way to do it
There's an &env arg, I think, that helps with this...
this is a great resource if you havenβt seen it before
Not sure if it helpful at all but I joted some notes a while back: https://github.com/lread/info-clj-cljs-variants
Also, think I used this pattern recently:
(defmacro my-macro []
(if (:ns &env)
;; target is cljs
`(js/console.log "Running in JS")
;; target is JVM Clojure
`(println "Running on the JVM")))let's try it like this, to sidestep the x/y problem
i have a macro that expands to a try/catch call. it analyzes each form given to the & body using a couple helper functions. on the jvm, this is really easy because i can write helper functions and then call them inside of the macro and they "just work".
as i have it written, there's assumptions about jvm clojure (`clojure.lang.ExceptionInfo` or Throwable, for example) that I need to use a reader conditional for, so I have stuff like (if (...) #?(:clj Throwable :cljs :default) ...) in these helper functions
but the helper functions are being called within jvm clojure, so it's choosing the :clj branch, not the :cljs branch
I can't rely on the (:ns &env) trick in a helper function because it's a normal function. i guess i could pass around a dialect arg, but isn't that just a hack for cljs's anemic macros?
(this also doesn't cover any other dialects lol but i'll burn that bridge when i get to it)
i could stuff everything into the macro, but the file is currently 165 lines long, i really don't want to inline all of that
so i'm confused on how to write macros in clojurescript with the same ease as in clojure
it feels like macros are second-class in clojurescript compared to clojure
They are mind-bendier, for sure. I'm no cljs expert, but here's what I figured out a while ago for test-doc-blocks: https://github.com/lread/test-doc-blocks/blob/main/src/lread/test_doc_blocks/runtime.cljc
Well, that you're running clj at macro time I think is part of the expectation
But yeah, don't expect to be working with the actual cljs exception at macro time
https://github.com/cgrand/macrovich tries to iron some of the bumps out
Then there was https://github.com/mfikes/chivorcam
Researching those really help to understand how things work
Oh @jcoyle already noted that one
@nbtheduke I think first you need to decide if the macro you wrote will be in a .cljc or in a .clj file - there is reference issue ro be taken care of in ns block. See #plumcp code that has many CLJC macros for example.
the goal is to use it in clj, cljs, and cljc files, with no friction
The use of macro can be anywhere, but the macro defined in a .cljc vs a .clj file has reference consequences at the call site. I personally like to define macros in .cljc files to keep everything together.
Hey Noah, this topic is completely beyond me but I just thought I'll let you know, in the Clojure test suite I recently https://github.com/jank-lang/clojure-test-suite/blob/main/test/clojure/core_test/portability.cljc#L74-99 of the (is (thrown? β¦)) where I needed to do extra ClojureScript stuff. Might be worth checking if that helps. Honestly dunno if it will though but thought I'll link to it π
.