Haskell での例外処理の続き。今日は例外を投げるよ!
throwIO
IO の中で、例外を投げるには throwIO を使います。
throwIO :: Exception e => e -> IO a
Exception型クラスのインスタンスを渡せばよさそうです。Control.Exceptionのマニュアルを読むと、Exception型クラスのインスタンスとして、IOException や ArithException があるのが分かります。
この中から、データ構成子が公開されているものを探してみましょう。ArithException は、データ構成子を公開していますね。その一つである、Overflow という例外を投げてみましょう。
> :m Control.Exception Control.Exception> throwIO Overflow *** Exception: arithmetic overflow
投げられました。
データ構成子が公開されてないものは、throwIO できません。非公開のデータ構成子を投げられるのは、そのライブラリの作者のみです。
自分で例外を作る (安直編)
IOException であれば、自分で例外を作れます。ややこしいんですが、IOException と Haskell 2010 で定義されている IOError は、単なる別名の関係にあります。IOError を作るには、userError を使います。
userError :: String -> IOError
見ての通り、文字列を渡すだけですね。
Control.Exception> throwIO $ userError "My error" *** Exception: user error (My error)
IOError は、データ構成子を公開していない抽象データ型なので、中身に触るには関数を使います。文字列を取り出すには、以下のようにします。
Control.Exception System.IO.Error> ioeGetErrorString $ userError "My error" "My error"
実際には、catch する関数で、このような操作をやることになります。
自分で例外を作る (本格編)
Haskell の例外は拡張可能です。自分で型を定義して、例外の仲間に入れてもらうことができます。やり方は以下の通りです。これがどういう意味だか考えてはいけません。とにかく、こうするのです。
{-# LANGUAGE DeriveDataTypeable #-} import Control.Exception as E import Data.Typeable data MyException = ThisException | ThatException deriving (Show, Typeable) instance Exception MyException
厳密に捕捉する
昨日は、あらゆる例外を捕捉する ignore という関数を catch に渡していました。今日は、例外を選んで catch してみましょう。次のようなハンドラを定義します。
arithHandler :: ArithException -> IO () arithHandler e = putStrLn $ "Arith " ++ show e ioHandler :: IOException -> IO () ioHandler e = putStrLn $ "IO " ++ show e
使ってみましょう。
> throwIO Overflow `E.catch` arithHandler `E.catch` ioHandler Arith arithmetic overflow > throwIO (userError "My error") `E.catch` arithHandler `E.catch` ioHandler IO user error (My error)
やったね! ところで、catch を連鎖するのは面倒なので、catches という関数も定義されています。
> throwIO Overflow `catches` [Handler arithHandler, Handler ioHandler] Arith arithmetic overflow
第三の多相性
さて、catches のコードは気持ち悪くありませんか? 型 ArithException -> IO () と型 IOException -> IO () という異なる型が、同じリストの中に入ってますよ!
このように、別々の型を同じ型に見せかけるのが、Haskell の第三の多相性 ExistentialQuantification です。簡単に言うと、クラスベースのオブジェクト指向の継承みたいな多相性です。 ArithException も IOException も、SomeException として扱えます。詳しくは Haskellの多相性とかHaskell vs OOPを読んで下さいね。