Как отловить исключение арности в Clojure? - PullRequest
0 голосов
/ 03 ноября 2018

Я пытаюсь поймать исключение 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 голосов
/ 03 ноября 2018

В некоторых случаях вы можете поймать 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/inc или clojure.lang.Numbers/unchecked_inc; в зависимости от состояния *unchecked-math*. Также обратите внимание, как начинается ошибка:

CompilerException clojure.lang.ArityException

В вашем примере вы не можете поймать прямо (inc), потому что этот вызов завершается с ошибкой в ​​ время компиляции , прежде чем код даже будет запущен. Он знает, что (inc) никогда не будет правильным, поэтому он немедленно завершится неудачей. Это хорошая вещь, хотя. (inc) никогда не будет верным , так что нет смысла пытаться его поймать.


Однако существуют обстоятельства, при которых перехват исключения арности может иметь смысл (хотя, вероятно, есть более эффективные способы решения проблемы *). Скажем, вы запрещаете вызову 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 просто накладывает пластырь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...