>import Control.Exception
>import System.Environment
>countLines :: String -> IO ()
>countLines fileName = do
> contents <- readFile fileName
> putStrLn $ "В этом файле " ++ show (length (lines contents)) ++
> " строк!"
>handler :: IOException -> IO ()
>handler e = putStrLn "У нас проблемы!"
>main = do
> (fileName:_) <- getArgs
> countLines fileName `catch` handler
Здесь мы определяем обработчик >handler
для всех исключений ввода-вывода и пользуемся функцией >catch
для перехвата исключения, возникающего в функции >countLines
.
Попробуем:
>$ ./linecount linecount.hs
>В этом файле 17 строк!
>$ ./linecount dont_exist.txt
>У нас проблемы!
Исключение ввода-вывода может быть вызвано целым рядом причин, среди которых, помимо отсутствия файла, может быть также отсутствие права на чтение файла или вообще отказ жёсткого диска. В обработчике мы не проверяли, какой вид исключения >IOException
получили. Мы просто возвращаем строку >"У
>нас
>проблемы"
, что бы ни произошло.
Простой перехват всех типов исключений в одном обработчике – плохая практика в языке Haskell, так же как и в большинстве других языков. Что если произошло какое-либо другое исключение, которое мы не хотели бы перехватывать, например прерывание программы? Вот почему мы будем делать то же, что делается в других языках: проверять, какой вид исключения произошёл. Если это тот вид, который мы ожидали перехватить, вызовем обработчик. Если это нечто другое, мы не мешаем исключению распространяться далее. Давайте изменим нашу программу так, чтобы она перехватывала только исключение, вызываемое отсутствием файла:
>import Prelude hiding (catch)
>import Control.Exception
>import System.Environment
>import System.IO.Error (isDoesNotExistError)
>countLines :: String -> IO () countLines fileName = do
> contents <- readFile fileName
> putStrLn $ "В этом файле " ++ show (length (lines contents)) ++
> " строк!"
>handler :: IOException -> IO ()
>handler e
> | isDoesNotExistError e = putStrLn "Файл не существует!"
> | otherwise = ioError e
>main = do
> (fileName:_) <- getArgs
> countLines fileName `catch` handler
Программа осталась той же самой, но поменялся обработчик, который мы изменили таким образом, что он реагирует только на одну группу исключений ввода-вывода. С этой целью мы воспользовались предикатом >isDoesNotExistError
из модуля >System.IO.Error
. Мы применяем его к исключению, переданному в обработчик, чтобы определить, было ли исключение вызвано отсутствием файла. В данном случае мы используем охранные выражения, но могли бы использовать и условное выражение