Исключение Clojure не перехвачено

Я изучаю Clojure и хочу создать макрос, который работает как try-catch в Java. Если выброшено исключение, оно должно быть возвращено. В противном случае должен быть возвращен результат. Любой открытый ресурс также должен быть закрыт в разделе finally (без использования with-open). Однако у меня есть одна проблема, кроме того, что меня не поймают, и я не знаю, как ее решить. Любая помощь или предложение будут оценены!

Вот мой код:

(defmacro my-try 
  ([expression] 
   `(try 
      ~expression
      (catch Exception e# e#)
      )
   )
  ([[value variable] expression] 
   `(let [~value ~variable] 
      (try
        ~expression
        (catch Exception e# e#)
        (finally (if (instance? java.io.Closeable ~value) (.close ~value)))
        )
      )
   )
  )

Когда я пытаюсь открыть и прочитать несуществующий файл:

(def v (my-try [s (FileReader. (File. "missing-file"))] (. s read)))
(println v)

Я получаю следующую ошибку:

Execution error (FileNotFoundException) at java.io.FileInputStream/open0 (FileInputStream.java:-2).
missing-file (Det går inte att hitta filen)

Я подозреваю, что это сообщение об ошибке является неперехваченным исключением, потому что (1) оно не соответствует структуре других исключений и (2) если я изменю формат возврата исключения, например добавлю конкретное сообщение, сообщение об ошибке все равно будет не изменить.

Любая помощь в поиске неисправности приветствуется! Большое спасибо за рассмотрение моей просьбы.


person robeng    schedule 05.01.2021    source источник


Ответы (1)


В (let [~value ~variable] ~variable оценивается как (FileReader. (File. "missing-file")), но это вне try. Получилось бы что-то вроде этого:

(let [s (FileReader. (File. "missing-file"))]
    (try
        (. s read)
        . . .
    ))

Переместите let в try, затем expression в тело let.


Кроме того, как и во всех языках, (catch Exception e# e#) в большинстве случаев является плохой идеей. Отбрасывание ошибок становится кошмаром, как только происходит что-то отдаленно нетривиальное, поскольку вы отбрасываете свое единственное свидетельство проблемы.

person Carcigenicate    schedule 05.01.2021
comment
Спасибо за ответ! Я думаю, вы имеете в виду что-то вроде этого: (let [~value ~variable] (~expression)). Если я это сделаю, не будет ли ~value в предложении finally не инстанцированным? - person robeng; 05.01.2021
comment
Да. Возможно, вам придется сделать что-то грязное, например, иметь volatile во внешнем let, а затем поместить value в try, а затем разыменовать его в finally (volatile похожи на atom без дополнительных затрат на безопасность). - person Carcigenicate; 05.01.2021
comment
Ok. Я посмотрю на это. Тем не менее, я попробовал это, и мне кажется, что это работает. Что вы думаете об этом решении: ([[value variable] expression] '(try (let [~value ~variable] (try ~expression (catch Exception e# (.toString e#)) (finally (if (instance? java.io.Closeable ~value) (.close ~value))))) (catch Exception e# (.toString e#)) ) ) Возможно, вам придется скопировать и отформатировать некоторые... - person robeng; 05.01.2021
comment
Я бы все равно избавился от except. Наличие finally определенно полезно для обеспечения того, чтобы файлы были закрыты (по сути, это то, что делает with в Python; вроде), но обработка исключений не должна выполняться, если вы точно не знаете, что можете правильно обработать ошибку. Перехватив ошибку и распечатав ее, вы не позволяете функциям, расположенным выше по стеку вызовов, обрабатывать ошибку от вашего имени (поскольку они могут быть в лучшем месте для восстановления, чем вы ниже), и вы также отбрасываете трассировки стека; бесценный ресурс при отладке. - person Carcigenicate; 05.01.2021