cljc宏中的错误处理

时间:2017-01-07 00:26:10

标签: clojure clojurescript

我发现在 clj cljs 中定义一个宏来进行错误处理是非常棘手的。我认为将Exceptionjs/Error进行交换是一件简单的事情,但事实证明它比这更复杂。

起初,我试过这个:

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

但每次都会产生Exception。我很快意识到问题是在编译我的 cljs 文件期间调用了宏,该文件发生在 clj 环境中。因此,我必须让宏返回一个表单,该表单将在运行时解析为正确的异常类。我试过这个:

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

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

现在它适用于 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#
        ,,,))))

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

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

更新

在ClojureMostly的帮助下,我像这样重构了宏,并且它有效:

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

2 个答案:

答案 0 :(得分:2)

执行此操作的常用方法是在&env中的特殊defmacro绑定中check for the :ns keyCopied from plumatic/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)))))

用法:

(macros/try-catchall (f)  (catch e# ::exception))

答案 1 :(得分:1)

您可以根据函数调用重构宏。这个函数会接受一个" thunk"表单并将其包装在try中(而不是直接在宏中执行此操作)。

例如:

(defmacro my-macro [& forms]
  `(my-macro* (fn []
                ~@forms)))

然后您可以将my-macro*定义为:

(defn my-macro* [f]
  (try
    (f)
    (catch #?(:clj Exception :cljs js/Error) e
      ...)))