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

и >Maybe для представления результатов, при вычислении которых может произойти ошибка.

Обработка исключений, возникших в чистом коде

В стандарте языка Haskell 98 года присутствует механизм обработки исключений ввода-вывода, который в настоящее время считается устаревшим. Согласно современному подходу все исключения, возникшие как при выполнении чистого кода, так и при осуществлении ввода-вывода, должны обрабатываться единообразно. Этой цели служит единая иерархия типов исключений из модуля >Control.Exception, в которую легко можно включать собственные типы исключений. Любой тип исключения должен реализовывать экземпляр класса типов >Exception. В модуле >Control.Exception объявлено несколько конкретных типов исключений, среди которых >IOException (исключения ввода-вывода), >ArithException (арифметические ошибки, например, деление на ноль), >ErrorCall (вызов функции >error), >PatternMatchFail (не удалось выбрать подходящий образец в определении функции) и другие.

Простейший способ выполнить действие, которое потенциально может вызвать исключение,– воспользоваться функцией >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 ())