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

.

Функция >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, позволяющих обработать исключение, в том числе и возникшее в чистом коде. Заметьте ещё раз, что вся обработка выполнялась в рамках действий ввода-вывода. Посмотрим теперь, как работать с исключениями, которые возникают при выполнении операций ввода-вывода.