如何在Clojure中捕获Arity异常?

时间:2018-11-03 02:04:11

标签: clojure exception-handling

我正在尝试捕获类似这样的Arity异常:

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

在这种情况下,我在调用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

尝试捕获任何Exception而不是clojure.lang.ArityException仍然会抛出该错误。

我相信任何有Clojure开发经验的人都会立即发现我的错误。

1 个答案:

答案 0 :(得分:6)

在某些情况下,您可以抓获ArityException个;取决于上下文和引起ArityException的功能。


我将立即承认我在某些方面进行推测,因为我以前从未对此进行过深入的探讨。

首先,看看在明显提供的参数数量错误的情况下调用inc会发生什么:

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

有趣的部分是inc--inliner--4489。如果您查看inc的定义,则会在其中附加以下元数据:

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

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

CompilerException clojure.lang.ArityException

在您的示例中,您无法直接捕获(inc),因为该调用在代码运行之前在编译时失败。它知道(inc)永远是不正确的,因此它立即失败。这是一件好事。 (inc) 永远是不正确的,因此无论如何都没有必要抓住它。


在某些情况下,捕获Arity异常 是有道理的(尽管可能有更好的方法来解决问题*)。假设您不允许调用inc内联,正如@Rulle建议的那样,apply插入参数:

(try
  (apply inc [])

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

Caught!

编译器无法确定inc是否会失败,因为它取决于提供给apply的参数的数量。这可能取决于运行时的情况。在这种情况下,该代码实际上可以运行,也可以捕获其ArityException


:inline排除在等式之外,您还可以看到您的自定义函数可以以较少的麻烦抓住其ArityException,因为该调用未内联,因此不会编译时失败:

(defn hello [arg]) 

(try
  (hello) ; No apply

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

Caught!

*我不能说我曾经不得不抓过ArityException,而且我也没有想到在任何情况下这样做都是合适的。即使您使用的是apply,提前验证参数还是重新考虑使用apply还是比较有意义的。如果使用apply导致ArityException,则可能是因为使用try只是将创可贴放在一边而在逻辑上存在缺陷。