【问题标题】:Error handling in cljc macrocljc 宏中的错误处理
【发布时间】:2017-01-07 00:26:09
【问题描述】:

我发现在 cljcljs 中定义一个宏来进行错误处理非常棘手。我以为将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,现在在运行时在 cljcljs 中解析为正确的异常类。

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

更新

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

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

【问题讨论】:

    标签: clojure clojurescript


    【解决方案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
          ...)))
    

    【讨论】:

    • 我忙于解决问题,我什至没有想到这个明显的解决方案!但是,ClojureMostly 的回答更直接地解决了我的问题,所以我接受了。
    【解决方案2】:

    执行此操作的常用方法是在 defmacro 中的特殊 &env 绑定中使用 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))
    

    【讨论】:

    • 你漏掉了if-cljs的定义,这是最重要的部分!此外,他们在这里所做的是根据环境返回完全不同的 try/catch 形式,这不是我想要做的。然而,经过一些调整,(:ns &env) 完全符合我的需要。我会用新代码更新我的问题。
    • 我想(检查:ns 键入&env)今天可以工作,甚至明天也可以工作。但绝对不能保证它会起作用。如果您实施此解决方案,重要的是要知道您依赖的是“未记录的行为”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 2016-08-18
    • 2010-10-11
    • 1970-01-01
    相关资源
    最近更新 更多