- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Изучай Haskell во имя добра! - Миран Липовача
Шрифт:
Интервал:
Закладка:
Простейший способ выполнить действие, которое потенциально может вызвать исключение,– воспользоваться функцией try:
try :: Exception e => IO a -> IO (Either e a)
Функция try пытается выполнить переданное ей действие ввода-вывода и возвращает либо Right <результат действия> либо Left <исключение>, например:
ghci> try (print $ 5 `div` 2) :: IO (Either ArithException ())
2
Right ()
ghci> try (print $ 5 `div` 0) :: IO (Either ArithException ())
Left divide by zero
Обратите внимание, что в данном случае потребовалось явно указать тип выражения, поскольку для вывода типа информации недостаточно. Помимо прочего, указание типа исключения позволяет обрабатывать не все исключения, а только некоторые. В следующем примере исключение функцией try обнаружено не будет:
> try (print $ 5 `div` 0) :: IO (Either IOException ())
*** Exception: divide by zero
Указание типа SomeException позволяет обнаружить любое исключение:
ghci> try (print $ 5 `div` 0) :: IO (Either SomeException ())
Left divide by zero
Попробуем написать программу, которая принимает два числа в виде параметров командной строки, делит первое число на второе и наоборот и выводит результаты. Нашей первой целью будет корректная обработка ошибки деления на ноль.
import Control.Exception
import System.Environment
printQuotients :: Integer -> Integer -> IO ()
printQuotients a b = do
print $ a `div` b
print $ b `div` a
params :: [String] -> (Integer, Integer)
params [a,b] = (read a, read b)
main = do
args <- getArgs
let (a, b) = params args
res <- try (printQuotients a b) :: IO (Either ArithException ())
case res of
Left e -> putStrLn "Деление на 0!"
Right () -> putStrLn "OK"
putStrLn "Конец программы"
Погоняем программу на различных значениях:
$ ./quotients 20 7
2
0
OK
Конец программы
$ ./quotients 0 7
0
Деление на 0!
Конец программы
$ ./quotients 7 0
Деление на 0!
Конец программы
Понятно, что пока эта программа неустойчива к другим видам ошибок. В частности, мы можем «забыть» передать параметры командной строки или передать их не в том количестве:
$ ./quotients
quotients: quotients.hs:10:1-31: Non-exhaustive patterns in function params
$ ./quotients 2 3 4
quotients: quotients.hs:10:1-31: Non-exhaustive patterns in function params
Это исключение генерируется при вызове функции params, если переданный ей список оказывается не двухэлементным. Можно также указать нечисловые параметры:
$ ./quotients a b
quotients: Prelude.read: no parse
Исключение здесь генерируется функцией read, которая не в состоянии преобразовать переданный ей параметр к числовому типу.
Чтобы справиться с любыми возможными исключениями, выделим тело программы в отдельную функцию, оставив в функции main получение параметров командной строки и обработку исключений:
mainAction :: [String] -> IO ()
mainAction args = do
let (a, b) = params args
printQuotients a b
main = do
args <- getArgs
res <- try (mainAction args) :: IO (Either SomeException ())
case res of
Left e -> putStrLn "Ошибка"
Right () -> putStrLn "OK"
putStrLn "Конец программы"
Мы были вынуждены заменить тип исключения на SomeException и сделать сообщение об ошибке менее информативным, поскольку теперь неизвестно, исключение какого вида в данном случае произошло.
$ ./quotients a b
Ошибка
Конец программы
$ ./quotients
Ошибка
Конец программы
Понятно, что в общем случае обработка исключения должна зависеть от её типа. Предположим, что у нас имеется несколько обработчиков для исключений разных типов:
handleArith :: ArithException -> IO ()
handleArith _ = putStrLn "Деление на 0!"
handleArgs :: PatternMatchFail -> IO ()
handleArgs _ = putStrLn "Неверное число параметров командной строки!"
handleOthers :: SomeException -> IO ()
handleOthers e = putStrLn $ "Неизвестное исключение: " ++ show e
К сожалению, чтобы увидеть исключение от функции read, нужно воспользоваться наиболее общим типом SomeException.
Вместо того чтобы вручную вызывать функцию обработчика при анализе результата try, можно применить функцию catch, вот её тип:
ghci> :t catch
catch :: Exception e => IO a -> (e -> IO a) -> IO a
ПРИМЕЧАНИЕ. Модуль Prelude экспортирует старую версию функции catch, которая способна обрабатывать только исключения ввода-вывода. Чтобы использовать новый вариант её определения, необходимо использовать скрывающий импорт: import Prelude hiding (catch).
Функция catch принимает в качестве параметров действие и обработчик исключения: если при выполнении действия генерируется исключение, то вызывается его обработчик. Тип обработчика определяет, какие именно исключения будут обработаны. Рассмотрим примеры, в которых функция mainAction вызывается непосредственно в GHCi:
ghci> mainAction ["2","0"]
*** Exception: divide by zero
ghci> mainAction ["0","2"] `catch` handleArith
0
Деление на 0!
ghci> mainAction ["2","0"] `catch` handleArgs
*** Exception: divide by zero
ghci> mainAction ["2","0"] `catch` handleOthers
Неизвестное исключение: divide by zero
ghci> mainAction ["a", "b"] `catch` handleArgs
*** Exception: Prelude.read: no parse
ghci> mainAction ["a", "b"] `catch` handleOthers
Неизвестное исключение: Prelude.read: no parse
Если строка, выводимая GHCi, начинается с ***, то соответствующее исключение не было обработано. Обратите внимание на обычный для функции catch инфиксный способ вызова. Заметьте также, что обработчик handleOthers способен обработать любое исключение.
Вернёмся к основной программе. Нам хочется, чтобы возникшее исключение было обработано наиболее подходящим образом: если произошло деление на ноль, то следует выполнить handleArith, при неверном числе параметров командной строки – handleArgs, в остальных случаях – handleOthers. В этом нам поможет функция catches, посмотрим на её тип:
> :t catches
catches :: IO a -> [Handler a] -> IO a
Функция catches принимает в качестве параметров действие и список обработчиков (функций, которые упакованы конструктором данных Handler) и возвращает результат действия. Если в процессе выполнения происходит исключение, то вызывается первый из подходящих по типу исключения обработчиков (поэтому, в частности, обработчик handleOthers должен быть последним). Перепишем функцию main так, чтобы корректно обрабатывались все возможные исключительные ситуации:
main = do
args <- getArgs
mainAction args `catches`
[Handler handleArith,
Handler handleArgs,
Handler handleOthers]
putStrLn "Конец программы"
Посмотрим, как она теперь работает:
$ ./quotients 20 10
2
0
Конец программы
$ ./quotients
Неверное число параметров командной строки!
Конец программы
$ ./quotients 2 0
Деление на 0!
Конец программы
$ ./quotients a b
Неизвестное исключение: Prelude.read: no parse
Конец программы
В этом разделе мы разобрались с работой функций try, catch и catches, позволяющих обработать исключение, в том числе и возникшее в чистом коде. Заметьте ещё раз, что вся обработка выполнялась в рамках действий ввода-вывода. Посмотрим теперь, как работать с исключениями, которые возникают при выполнении операций ввода-вывода.
Обработка исключений ввода-вывода
Исключения ввода-вывода происходят, когда что-то пошло не так при взаимодействии с внешним миром в действии ввода-вывода, являющемся частью функции main. Например, мы пытаемся открыть файл, и тут оказывается, что он был удалён, или ещё что-нибудь в этом духе. Посмотрите на программу, открывающую файл, имя которого передаётся в командной строке, и говорящую нам, сколько строк содержится в файле:
import System.Environment
import System.IO
main = do
(fileName:_) <– getArgs
contents <– readFile fileName
putStrLn $ "В этом файле " ++ show (length (lines contents)) ++
" строк!"
Очень простая программа. Мы выполняем действие ввода-вывода getArgs и связываем первую строку в возвращённом списке с идентификатором fileName. Затем связываем имя contents с содержимым файла. Применяем функцию lines к contents, чтобы получить список строк, считаем их количество и передаём его функции show, чтобы получить строковое представление числа. Это работает – но что получится, если передать программе имя несуществующего файла?

