Опубликован: 19.09.2008 | Уровень: специалист | Доступ: платный
Лекция 8:

Основные операции ввода - вывода

< Лекция 7 || Лекция 8: 12 || Лекция 9 >

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" .

< Лекция 7 || Лекция 8: 12 || Лекция 9 >
KroshkaRu KroshkaRu
KroshkaRu KroshkaRu
Россия, Петерубрг, СПБ-ГПУ, 1998
Петр Бондареко
Петр Бондареко
Россия