我发现在clj和cljs中定义一个宏来进行错误处理是非常棘手的。我以为这是一个简单的问题,将Exception与js/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不允许间接引用异常类,而ClojureScript允许引用异常类呢?
更新
在ClojureMostly的帮助下,我对宏进行了如下重构,它运行如下:
(defmacro my-macro
[& forms]
`(try
~@forms
(catch ~(if (:ns &env) 'js/Error 'Exception) e#
,,,)))发布于 2017-01-09 14:53:22
这样做的常见方法是在 key中使用defmacro中的特殊&env绑定。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))发布于 2017-01-09 05:31:38
您可以重构您的宏以函数调用的形式表示。该函数将接受表单的"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
...)))https://stackoverflow.com/questions/41516492
复制相似问题