finally
и onException
- две функции из модуля Control.Exception
, которые имеют одинаковую сигнатуру, но ведут себя по-разному.
Здесь - это документ.
Для finally
написано:
finally
:: IO a -- computation to run first
-> IO b -- computation to run afterward (even if an exception was raised)
-> IO a
, а для onException
написано:
Как и finally
, но выполняет только последнее действие, если
исключение вызвано вычислением.
Итак, я делаю следующий тест:
ghci> finally (return $ div 4 2) (putStrLn "Oops!")
Oops!
2
ghci> finally (return $ div 4 0) (putStrLn "Oops!")
Oops!
*** Exception: divide by zero
, который действует как ожидалось.
Однако onException
не:
ghci> onException (return $ div 4 2) (putStrLn "Oops!")
2
ghci> onException (return $ div 4 0) (putStrLn "Oops!") -- does not act as expected
*** Exception: divide by zero
Как описано выше, onException
выполняет окончательное действие только в случае возникновения исключения, но приведенный выше пример показывает, что onException
не выполняет окончательное действие, т.е. putStrLn "Oops!"
при возникновении исключения.
После проверки исходного кода для onException
я пытаюсь выполнить тест следующим образом:
ghci> throwIO (SomeException DivideByZero) `catch` \e -> do {_ <- putStrLn "Oops!"; throwIO (e :: SomeException)}
Oops!
*** Exception: divide by zero
ghci> onException (throwIO (SomeException DivideByZero)) (putStrLn "Oops!")
Oops!
*** Exception: divide by zero
Как видно, когда явное исключение возникло, было выполнено последнее действие.
Таким образом, вопрос return $ div 4 0
действительно вызывает исключение, но почему onException (return $ div 4 0) (putStrLn "Oops!")
не выполняет окончательное действие putStrLn "Oops!"
? Что мне не хватает? А как было выполнено исключение?
ghci> throwIO (SomeException DivideByZero)
*** Exception: divide by zero
ghci> (return $ div 4 0) :: IO Int
*** Exception: divide by zero