Россия, Петерубрг, СПБ-ГПУ, 1998 |
Основные операции ввода - вывода
7.2. Последовательные операции ввода - вывода
Конструктор типа IO является экземпляром класса Monad. Две монадические связывающие функции, методы в классе Monad, используются для составления последовательностей операций ввода - вывода. Функция >> используется там, где результат первой операции не представляет интереса, например, когда он представляет собой (). Операция >>= передает результат первой операции в качестве аргумента второй операции.
(>>=) :: IO a -> (a -> IO b) -> IO b (>>) :: IO a -> IO b -> IO b
main = readFile "input-file" >>= \ s -> writeFile "output-file" (filter isAscii s) >> putStr "Фильтрация завершилась успешно\n"
похожа на предыдущий пример, использующий interact, но получает свой ввод из "input-file" и записывает свой вывод в "output-file". Перед завершением программы на стандартный вывод распечатывается сообщение.
Нотация do позволяет программировать в более императивном синтаксическом стиле. Слегка более сложной версией предыдущего примера была бы программа:
main = do putStr "Файл ввода: " ifile <- getLine putStr "Файл вывода: " ofile <- getLine s <- readFile ifile writeFile ofile (filter isAscii s) putStr "Фильтрация завершилась успешно\n"
Функция return используется для определения результата операции ввода - вывода. Например, getLine определена в терминах getChar, используя return для определения результата:
getLine :: IO String getLine = do c <- getChar if c == '\n' then return "" else do s <- getLine return (c:s)
7.3. Обработка исключений в монаде ввода - вывода
Монада ввода - вывода включает простую систему обработки исключений. Любая операция ввода - вывода может вызвать исключение вместо возвращения результата.
Исключения в монаде ввода - вывода представлены значениями типа IOError. Это абстрактный тип: его конструкторы скрыты от пользователя. Библиотека IO определяет функции, которые конструируют и изучают значения IOError . Единственной функцией Prelude, которая создает значение IOError, является userError. Пользовательские значения ошибок включают строку с описанием ошибки.
userError :: String -> IOError
Исключения вызываются и отлавливаются с помощью следующих функций:
ioError :: IOError -> IO a catch :: IO a -> (IOError -> IO a) -> IO a
Функция ioError вызывает исключение; функция catch устанавливает обработчик, который получает любое исключение, вызванное действием, защищенным catch. Исключение отлавливается самым последним обработчиком, установленным catch. Эти обработчики не действуют выборочно: они отлавливают все исключения.
Распространение исключения нужно явно обеспечить в обработчике путем повторного вызова любого нежелательного исключения. Например, в
f = catch g (\e -> if IO.isEOFError e then return [] else ioError e)
функция f возвращает [], когда в g возникает исключение конца файла, иначе исключение передается следующему внешнему обработчику. Функция isEOFError является частью библиотеки IO.
Когда исключение передается за пределы главной программы, система Haskell выводит связанное с ним значение IOError и выходит из программы.
Метод fail экземпляра IO класса Monad (раздел "6.3.6" ) вызывает userError так:
instance Monad IO where ...bindings for return, (>>=), (>>) fail s = ioError (userError s)
Исключения, вызванные функциями ввода - вывода в Prelude, описаны в лекции "21" .