Использование функции withFile
То, что мы только что сделали, можно сделать и по-другому – с использованием функции withFile. Сигнатура этой функции:
withFile :: FilePath –> IOMode –> (Handle –> IO a) –> IO a
Она принимает путь к файлу, режим открытия файла и некоторую функцию, принимающую дескриптор и возвращающую некое действие ввода-вывода. Функция withFile вернёт действие ввода-вывода, которое откроет файл, сделает с ним то, что нам нужно, и закроет его. Результат, помещённый в заключительном действии ввода-вывода, будет взят из результата переданной нами функции. С виду это может показаться сложным, но на самом деле всё просто, особенно если использовать анонимные функции. Вот как можно переписать предыдущий пример с использованием функции withFile:
import System.IO
main = do
withFile "girlfriend.txt" ReadMode (handle –> do
contents <– hGetContents handle
putStr contents)
Функция (handle -> …) принимает дескриптор файла и возвращает действие ввода-вывода. Обычно пишут именно так, пользуясь анонимной функцией. Нам действительно нужна функция, возвращающая действие ввода-вывода, а не просто выполнение некоторого действия и последующее закрытие файла, поскольку действие, переданное функции withFile, не знало бы, с каким файлом ему необходимо работать. Сейчас же функция withFile открывает файл, а затем передаёт его дескриптор функции, которую мы ей передали. Функция возвращает действие ввода-вывода, на основе которого withFile создаёт новое действие, работающее почти так же, как и исходное, но с добавлением гарантированного закрытия файла даже в тех случаях, когда что-то пошло не так.