异常#

简介#

  • Haskell 中的某些类型,如MaybeEither能够处理部分运算异常,但不够强大;

  • Haskell 函数是无副作用的,因此异常处理在 I/O 操作部分更有意义;

  • 纯代码部分同样可以抛出异常;

    Prelude> 4 `div` 0
    *** Exception: divide by zero
    
  • 因为纯代码部分的状态是透明的,因此异常只能在 I/O 操作部分捕获;

  • 应尽量保持 I/O 操作部分代码最小化,利用 Haskell 的类型系统避免异常;

捕获#

System.IO.Error.catchIOError :: IO a -> (IOError -> IO a) -> IO a#

若 I/O 操作抛出异常,则将异常传入异常处理函数,并返回处理结果。

base-4.4.0.0之前,该函数名为catch

IOError类型表示 I/O 操作异常,带有异常信息,该类型的实现取决于语言的实际实现。

 1import System.Environment
 2import System.IO
 3import System.IO.Error
 4
 5main = toTry `catchIOError` handler
 6
 7-- | 程序的核心。
 8-- 计算文件有多少行。
 9toTry :: IO ()
10toTry = do (fileName : _) <- getArgs
11           contents       <- readFile fileName
12           let lnum = show . length . lines $ contents
13           putStrLn $ "The file has " ++ lnum ++ " lines!"
14
15-- | 处理 'IOError'。
16-- 若打开文件时出现异常,则打印信息。
17handler :: IOError -> IO ()
18handler _ = putStrLn "Exception!"
GHC.IO.Exception.ioError :: IOError -> IO a#

将捕获的异常重新抛出。

System.IO.Error.isAlreadyExistsError :: IOError -> Bool#
System.IO.Error.isAlreadyInUseError :: IOError -> Bool#
System.IO.Error.isDoesNotExistError :: IOError -> Bool#
System.IO.Error.isEOFError :: IOError -> Bool#
System.IO.Error.isFullError :: IOError -> Bool#
System.IO.Error.isIllegalOperation :: IOError -> Bool#
System.IO.Error.isPermissonError :: IOError -> Bool#
System.IO.Error.isResourceVanishedError :: IOError -> Bool#
System.IO.Error.isUserError :: IOError -> Bool#

判断异常是否为对应异常。

 1import System.Environment
 2import System.IO
 3import System.IO.Error
 4
 5main = toTry `catchIOError` handler
 6
 7-- | 程序的核心。
 8-- 计算文件有多少行。
 9toTry :: IO ()
10toTry = do (fileName : _) <- getArgs
11           contents       <- readFile fileName
12           let lnum = show . length . lines $ contents
13           putStrLn $ "The file has " ++ lnum ++ " lines!"
14
15-- | 处理 'IOError'。
16-- 若有已知异常,则发出警告,否则抛出。
17handler :: IOError -> IO ()
18handler e | isDoesNotExistError e = putStrLn "No such file!"
19          | isFullError e         = putStrLn "Disk full!"
20          | isIllegalOperation e  = putStrLn "Illegal move!"
21          | otherwise             = ioError e
System.IO.Error.ioeGetErrorString :: IOError -> String#
System.IO.Error.ioeGetErrorType :: IOError -> IOErrorType#
System.IO.Error.ioeGetFileName :: IOError -> Maybe FilePath#
System.IO.Error.ioeGetHandle :: IOError -> Maybe GHC.IO.Handle.Types.Handle#
System.IO.Error.ioeGetLocation :: IOError -> String#

根据传入的异常获取异常的属性。

System.IO.Error.ioeSetErrorString :: IOError -> String#
System.IO.Error.ioeSetErrorType :: IOError -> IOErrorType#
System.IO.Error.ioeSetFileName :: IOError -> Maybe FilePath#
System.IO.Error.ioeSetHandle :: IOError -> Maybe GHC.IO.Handle.Types.Handle#
System.IO.Error.ioeSetLocation :: IOError -> String#

根据传入的异常设置异常的属性。

 1import System.Environment
 2import System.IO
 3import System.IO.Error
 4
 5main = toTry `catchIOError` handler
 6
 7-- | 程序的核心。
 8-- 计算文件有多少行。
 9toTry :: IO ()
10toTry = do (fileName : _) <- getArgs
11           contents       <- readFile fileName
12           let lnum = show . length . lines $ contents
13           putStrLn $ "The file has " ++ lnum ++ " lines!"
14
15-- | 处理 'IOError'。
16-- 若有已知异常,则发出警告,否则抛出。
17handler :: IOError -> IO ()
18handler e
19  | isDoesNotExistError e = case ioeGetFileName e of
20    Just path ->
21      let w = show path ++ " " ++ ioeGetErrorString e ++ "."
22      in  putStrLn w
23    Nothing -> putStrLn "No such file."
24  | otherwise = ioError e