I found it surprisingly tricky to define a macro to do error handling in both clj and cljs. I assumed it was a simple matter of swapping Exception
with js/Error
, but it turned out to be more complicated than that.
At first, I tried this:
(defmacro my-macro
[& forms]
`(try
~@forms
(catch #?(:clj Exception :cljs js/Error) e#
,,,)))
But this produced Exception
every time. I soon realized that the problem was that the macro was being called during the compilation of my cljs files, which happens in a clj environment. Therefore, I would have to have the macro return a form that would resolve to the correct exception class at runtime. I tried this:
(def exception-class
#?(:clj Exception :cljs js/Error))
(defmacro my-macro
[& forms]
`(try
~@forms
(catch exception-class e#
,,,)))
Now it worked in cljs, but not in clj!!! After some experimentation, I discovered that JVM Clojure (apparently) does not allow you to refer to the exception class indirectly. You have to refer to Exception
directly by name.
So finally, I settled on this:
(def fake-java
#?(:cljs (clj->js {:lang {:Exception js/Error}})))
(defmacro my-macro
[& forms]
`(let [~'java fake-java]
(try
~@forms
(catch Exception e#
,,,))))
Exception
expands to java.lang.Exception
, which now resolves to the correct exception class at runtime in both clj and cljs.
My question is, is there a better way to this? And why does JVM Clojure not allow referring to the exception class indirectly, but ClojureScript does?
Update
With ClojureMostly's help, I have refactored the macro like this, and it works:
(defmacro my-macro
[& forms]
`(try
~@forms
(catch ~(if (:ns &env) 'js/Error 'Exception) e#
,,,)))
You could refactor your macro to be expressed in terms of a function call. This function would accept a "thunk" of the forms and wrap it in the try
(rather than doing this directly in the macro).
For example:
(defmacro my-macro [& forms]
`(my-macro* (fn []
~@forms)))
Then you can define my-macro*
as:
(defn my-macro* [f]
(try
(f)
(catch #?(:clj Exception :cljs js/Error) e
...)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With