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