如何在Clojure中捕获arity异常?

dch*_*cke 2 exception-handling clojure

我试图像这样捕获一个arity异常:

(try
  (inc)
  (catch clojure.lang.ArityException e
    (str "caught exception: " (.getMessage e))))))
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我调用时inc没有传递数字,它正确地引发异常.但是,当我运行它时,该异常不会被捕获:

(try
  (inc)
  (catch clojure.lang.ArityException e
    (str "caught exception: " (.getMessage e))))))

; => CompilerException clojure.lang.ArityException: Wrong number of args (0) passed to: core/inc--inliner--4489
Run Code Online (Sandbox Code Playgroud)

试图抓住任何Exception一般而不是clojure.lang.ArityException仍然抛出它.

我相信任何在Clojure开发中经验丰富的人都会立即抓住我的错误.

Car*_*ate 6

ArityException在某些情况下你可以抓住; 取决于上下文和导致的功能ArityException.


我将承认我正在推测某些方面,因为我之前从未深入探讨过这个问题.

首先,看看如果你inc在一个很明显提供的参数数量错误的上下文中调用会发生什么:

(inc)
CompilerException clojure.lang.ArityException: Wrong number of args (0) passed to: core/inc--inliner--4489, compiling: . . . 
Run Code Online (Sandbox Code Playgroud)

有趣的是inc--inliner--4489.如果你查看定义inc,它会附加这个元数据:

:inline (fn [x] `(. clojure.lang.Numbers (~(if *unchecked-math* 'unchecked_inc 'inc) ~x)))
Run Code Online (Sandbox Code Playgroud)

我之前从未:inline深入研究过,但我一直认为这意味着它会尝试内联调用(略微)减少开销.在这种情况下,它试图内联inc只是一个调用clojure.lang.Numbers/inc,或clojure.lang.Numbers/unchecked_inc; 取决于的状态*unchecked-math*.还要注意错误是如何开始的:

CompilerException clojure.lang.ArityException
Run Code Online (Sandbox Code Playgroud)

在您的示例中,您无法直接捕获,(inc)因为该调用在代码运行之前的编译时失败.它知道(inc)永远不会是正确的,所以它会立即失败.这是一件好事.(inc) 永远是正确的,所以有,想抓住它反正是没有意义的.


然而,有些情况下捕获一个arity异常可能有意义(尽管可能有更好的方法来解决问题*).假设您不允许inc内联调用,如@Rulle所建议的那样apply:

(try
  (apply inc [])

  (catch clojure.lang.ArityException e
    (println "Caught!")))

Caught!
Run Code Online (Sandbox Code Playgroud)

编译器无法确定是否inc会失败,因为它取决于提供的参数数量apply; 这可能取决于运行时的事情.在这种情况下,代码实际上能够运行,并且还能够ArityException捕获它.


考虑:inline的方程,你也可以看到你的自定义函数都可以有ArityException较少的大惊小怪抓住,因为呼叫没有内联,所以它不会在编译时失败:

(defn hello [arg]) 

(try
  (hello) ; No apply

  (catch clojure.lang.ArityException e
    (println "Caught!")))

Caught!
Run Code Online (Sandbox Code Playgroud)

*我不能说我曾经不得不抓住一个ArityException,我想不出任何情况这样做是合适的.即使你正在使用apply,提前验证参数,甚至重新考虑你的使用会更有意义apply.如果使用apply导致a ArityException,你可能在你的逻辑中有一个缺陷,使用的try只是放置一个创可贴.