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