cljc宏中的错误处理

gra*_*ero 5 clojure clojurescript

我发现定义一个宏同时在cljcljs中进行错误处理非常棘手。我以为这是交换的一个简单的事情Exceptionjs/Error,但它竟然比更复杂。

一开始,我尝试过这样:

(defmacro my-macro
  [& forms]
 `(try
    ~@forms
    (catch #?(:clj Exception :cljs js/Error) e#
      ,,,)))
Run Code Online (Sandbox Code Playgroud)

但这Exception每次都会产生。我很快意识到问题是在我的cljs文件的编译过程中调用了宏,这种情况发生在clj环境中。因此,我将不得不让宏返回一种形式,该形式将在运行时解析为正确的异常类。我尝试了这个:

(def exception-class
  #?(:clj Exception :cljs js/Error))

(defmacro my-macro
  [& forms]
 `(try
    ~@forms
    (catch exception-class e#
      ,,,)))
Run Code Online (Sandbox Code Playgroud)

现在它可以在cljs中工作,但不能在clj中工作!经过一些试验,我发现JVM Clojure(显然)不允许您间接引用异常类。您必须Exception直接通过名称引用。

所以最后,我决定了:

(def fake-java
  #?(:cljs (clj->js {:lang {:Exception js/Error}})))

(defmacro my-macro
  [& forms]
 `(let [~'java fake-java]
    (try
      ~@forms
      (catch Exception e#
        ,,,))))
Run Code Online (Sandbox Code Playgroud)

Exception扩展到java.lang.Exception,现在可以在运行时在cljcljs中解析为正确的异常类。

我的问题是,有更好的方法吗?为什么JVM Clojure不允许间接引用异常类,而ClojureScript允许间接引用呢?

更新资料

在ClojureMostly的帮助下,我已经像这样重构了宏,并且可以正常工作:

(defmacro my-macro
  [& forms]
 `(try
    ~@forms
    (catch ~(if (:ns &env) 'js/Error 'Exception) e#
      ,,,)))
Run Code Online (Sandbox Code Playgroud)

Clo*_*tly 4

执行此操作常见方法是检查:ns. 复制自&envdefmacroplumatic/schema

(defn cljs-env?
  "Take the &env from a macro, and tell whether we are expanding into cljs."
  [env]
  (boolean (:ns env)))

(defmacro try-catchall
  "A cross-platform variant of try-catch that catches all exceptions.
   Does not (yet) support finally, and does not need or want an exception class."
  [& body]
  (let [try-body (butlast body)
        [catch sym & catch-body :as catch-form] (last body)]
    (assert (= catch 'catch))
    (assert (symbol? sym))
    (if (cljs-env? &env)
      `(try ~@try-body (~'catch js/Object ~sym ~@catch-body))
      `(try ~@try-body (~'catch Throwable ~sym ~@catch-body)))))
Run Code Online (Sandbox Code Playgroud)

用法:

(macros/try-catchall (f)  (catch e# ::exception))
Run Code Online (Sandbox Code Playgroud)