Вспомогательные функции для работы с исключениями

Ранее в этой главе мы уже познакомились с функциями bracket и bracketOnError, которые реализуют наиболее часто используемый сценарий обработки исключений, когда работа с ресурсом состоит из трёх стадий:

• получение ресурса;

• использование ресурса;

• освобождение ресурса.

В наших примерах на первой стадии открывался файл, на второй шла работа с его содержимым, а на третьей файл закрывался. Функция bracket гарантировала выполнение всех трёх действий, даже если в процессе генерировалось исключение, а функция bracketOnError запускала третье действие только в случае возникновения исключения.

Обратите внимание, что программист, использующий такого рода функции, не работает непосредственно с исключениями – ему лишь достаточно понимать логику и порядок вызова конкретных действий.

Модуль Control.Exception содержит ещё несколько подобных функций. Функция finally обеспечивает гарантированное выполнение некоторого действия по завершении другого действия. Это всего навсего упрощённый вариант функции bracket. Вот её сигнатура:

finally :: IO a -> IO b -> IO a

В следующем примере текст "Готово!" печатается в каждом из двух случаев, несмотря на возникновение исключения во втором:

ghci> print (20 `div` 10) `finally` putStrLn "Готово!"

2

Готово!

ghci> print (2 `div` 0) `finally` putStrLn "Готово!"

Готово!

*** Exception: divide by zero

Функция onException позволяет выполнить заключительное действие только в случае возникновения исключения:

ghci> print (20 `div` 10) `onException` putStrLn "Ошибка!"

2

ghci> print (2 `div` 0) `finally` putStrLn "Ошибка!"

Ошибка!

*** Exception: divide by zero

Заметьте, что обе эти функции, в отличие от try или catch, не обрабатывают исключения – они лишь гарантируют выполнение указанных действий. Все эти функции нетрудно реализовать вручную, пользуясь лишь try или catch. Фактически они устанавливают свой обработчик, перехватывают исключение, выполняют заданные действия, а после этого повторно генерируют то же самое исключение. Тем не менее, если ваша задача соответствует одному из приведённых сценариев, стоит воспользоваться уже существующей функцией.