catch で例外を捕捉してみる。

第25回 Haskell流の例外処理を学ぶ を参考に例外を捕捉してみます。

haskellでの例外処理は

catch (例外が発生する可能性のある関数) (例外が起きた場合に実行する関数)

という書き方をします。
「ファイルを開いて表示する」という処理を実行しようとしたとき、ファイル名が間違っていたり、ファイルがなかったりするとエラーが発生してプログラムが停止してしまいますが、その例外を補足してみます。

module Exception where
import Control.Exception
import Prelude hiding (catch)

fRead :: FilePath -> IO ()
fRead fileName = catch (showFile fileName) (showError fileName)

showFile :: FilePath -> IO ()
showFile file = putStr =<< readFile file

showError :: String -> SomeException -> IO ()
showError file e = do
  return (e::SomeException)
  putStrLn $ "File:" ++ file ++ " does not exist (No such file or directory)"

catch 関数は第一引数にIO型の関数、第二引数にExceptionクラスのインスタンスを引数にとり、IO型を返します。関数が実行するのはここまでで、第一引数のIO型か第二引数のIO型が返されるのでHaskellの実行環境がそのアクションを関数とは離れた外部で実行する、というイメージでしょうか。

catch :: (Exception e) => IO a -> (e -> IO a) -> IO a

二番目の引数 (showError fileName) には Exceptionクラスのインスタンスが渡されています。この型が「不特定多数の例外を表現する型」なので (e::SomeException) で型を強制しています。(第25回 Haskell流の例外処理を学ぶ
通常の言語ですと return (e::SomeException) で showError 関数を抜けてしまいそうですが・・・そうではないようで、IO型にくるんでいるようです。

ファイルがあれば表示

ghci> fRead "exception.hs"
module Exception where
  (snip)
  putStrLn $ "File:" ++ file ++ " does not exist (No such file or directory)"

ファイルがなければエラー表示

ghci> fRead "exception.hss"
File:exception.hss does not exist (No such file or directory)

もっと上のレイヤから呼んでみる。

ghci> (\f->catch (fRead f) (showError f)) "exception.hs"
module Exception where
import Control.Exception
import Prelude hiding (catch)
      (snip)

ghci> (\f->catch (fRead f) (showError f)) "exception.hss"
File:exception.hss does not exist (No such file or directory)