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

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