Изучай Haskell во имя добра! (Липовача) - страница 133

Функция >(\handle>->>…) принимает дескриптор файла и возвращает действие ввода-вывода. Обычно пишут именно так, пользуясь анонимной функцией. Нам действительно нужна функция, возвращающая действие ввода-вывода, а не просто выполнение некоторого действия и последующее закрытие файла, поскольку действие, переданное функции >withFile, не знало бы, с каким файлом ему необходимо работать. Сейчас же функция >withFile открывает файл, а затем передаёт его дескриптор функции, которую мы ей передали. Функция возвращает действие ввода-вывода, на основе которого >withFile создаёт новое действие, работающее почти так же, как и исходное, но с добавлением гарантированного закрытия файла даже в тех случаях, когда что-то пошло не так.

Время заключать в скобки

Обычно, если какой-нибудь фрагмент кода вызывает функцию >error (например, когда мы пытаемся вызвать функцию >head для пустого списка) или случается что-то плохое при вводе-выводе, наша программа завершается с сообщением об ошибке. В таких обстоятельствах говорят, что произошло исключение. Функция >withFile гарантирует, что независимо от того, возникнет исключение или нет, файл будет закрыт.



Подобные сценарии встречаются довольно часто. Мы получаем в распоряжение некоторый ресурс (например, файловый дескриптор), хотим с ним что-нибудь сделать, но кроме того хотим, чтобы он был освобождён (файл закрыт). Как раз для таких случаев в модуле >Control.Exception имеется функция >bracket. Вот её сигнатура:

>bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c

Первым параметром является действие, получающее ресурс (дескриптор файла). Второй параметр – функция, освобождающая ресурс. Эта функция будет вызвана даже в случае возникновения исключения. Третий параметр – это функция, которая также принимает на вход ресурс и что-то с ним делает. Именно в третьем параметре и происходит всё самое важное, а именно: чтение файла или его запись.

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

>withFile :: FilePath –> IOMode –> (Handle –> IO a) –> IO a

>withFile name mode f = bracket (openFile name mode)

>   (\handle -> hClose handle)

>   (\handle -> f handle)

Первый параметр, который мы передали функции >bracket, открывает файл; результатом является дескриптор. Второй параметр принимает дескриптор и закрывает его. Функция >bracket даёт гарантию, что это произойдёт, даже если возникнет исключение. Наконец, третий параметр функции