Haskell の例外処理
catch 関数
ghci から存在しないファイルを読み出そうとすると、エラーメッセージが出る。Prelude> readFile "not_exist" *** Exception: not_exist: openFile: does not exist (No such file or directory)
例外が発生しているわけだが、これをプログラムの中で捉えて例外処理をしてプログラムが止まってしまわないようにするためには catchIOError 関数を使う。catchIOError 関数は System.IO.Error モジュールの関数だ。このモジュールは IO モナド専用の例外処理の関数を納めているモジュールだ。catchIOError 関数の型は次のようになる。
Prelude> :m System.IO.Error Prelude System.IO.Error> :t catchIOError catchIOError :: IO a -> (IOError -> IO a) -> IO a
つまり、catch 関数は第1引数に IO a 型のデータをとり、第2引数に、「IOErro 型の引数をとり、戻り値が IO a 型の関数」をとる。そこで、実験をしてみた。
Prelude System.IO.Error> catchIOError (readFile "not_exist") (\e -> return "Exception") "Exception"
ファイル読み取りエラーで発生した例外 e を捕捉し、"Exception" というメッセージの文字列を catchIOError 関数の値として返している。これを利用すると、IO モナドの中で発生した例外を補足して処理を中断させないようにできる。
例外 e は IOError 型のデータだが、エラー情報を保持している。System.IO.Error モジュールにはその情報を取り出す関数も定義されているが、IO モナドの例外処理の概略をとりあえず知るためには煩雑になるのでここでは述べない。
ユーザ定義の IO Error
ユーザー定義の IOError 型のデータを作るときは userError 関数を使う。
Prelude System.IO.Error> userError "user exception" user error (user exception)
これを使って例外処理を発生させるには、ioError 関数を使う。
Prelude System.IO.Error> ioError $ userError "user exception" *** Exception: user error (user exception)
同じことは fail 関数でも行える。
Prelude System.IO.Error> fail "user exception" *** Exception: user error (user exception)
少しプログラムらしい使い方をしてみた。
Prelude System.IO.Error> catchIOError (do putStrLn "in catch"; fail "now out") (\e -> return $ ioeGetErrorString e) in catch "now out"
上のプログラムの ioeGetErrorString 関数は、例外 e のフィールドのメッセージ文字列を取り出す関数だ。ioeGetErroString 関数は System.IO.Error モジュールで定義されている。