Prev: Maybe モナド | TOC: 目次 | Next: List モナド |
計算のタイプ: | 失敗あるいは例外を投げる計算 |
---|---|
束縛戦略: | 失敗はその失敗が発生した理由/場所に関する情報を記録する。 失敗値は束縛された関数をバイパスし、それ以外の値は束縛された関数の 入力として使われます。 |
利用場面: | 失敗あるいはエラー処理を構造化する例外処理をつかう関数のならびから 計算を構築する。 |
ゼロとプラス: | ゼロは空のエラーにより表現され、プラス演算は最初の引数が失敗すれば、 2つめの引数を実行します。 |
型の例: | Either String a |
Error モナド(Exception モナドともいいます)は、 束縛された関数をバイパスすることで、発生したところから、 それを処理する処理するところへ例外を投げることができる 計算を、合成する戦略を内包しています。
MonadError
クラスはエラー情報の型とモナド型構築子
によってパラメータ化されています。Either String
を
エラー内容の記述が文字列形式であるエラーモナド用のモナド型構築子
として使うのが共通しています。そのケースやその他のよくあるケースでは
結果のモナドはすでに MonadError
クラスのインスタンスとして
定義されています。自分用のエラーの型を定義することもできますし、
Either String
あるいは
Either IOError
以外のモナド型構築子を使えます。
これらのケースでは、Error
および/あるいは
MonadError
クラスのインスタンスを明示的に定義する必要が
あります。
以下の MonadError
クラスの定義は複数パラメータの型クラスと
funDep という標準の Haskell 98 にはない言語拡張をつかっています。
MonadError
クラスの利点を得るのにこれを理解する必要はありません。
class Error a where noMsg :: a strMsg :: String -> a class (Monad m) => MonadError e m | m -> e where throwError :: e -> m a catchError :: m a -> (e -> m a) -> m a |
throwError
はモナド計算内で例外処理を始めるために
使われます。catchError
は直前のエラーを処理し、通常の
実行に復帰するためのハンドラ関数を提供します。
よくつかわれるイディオムは、
do { action1; action2; action3 } `catchError` handlerです。ここで、
action
関数は throwError
を
呼ぶことができます。handler
およびその do ブロックは
同じ返り値型をもたなければなりません。
MonadError
クラスのインスタンスとして定義された
Either e
型構築子は直截的なものです。以下の
慣習では、Left
はエラー値に使い、Right
は
非エラー値(正しい値)に使います。
instance MonadError (Either e) where throwError = Left (Left e) `catchError` handler = handler e a `catchError` _ = a |
次の例は、ErrorMonad
の throwError
および
catchError
の例外機構を使ってカスタマイズした
Error
データ型の使い方を示したものです。
この例は16進数をパースし、不正な文字に出会うと例外を投げます。
パースエラーの箇所を記録するために特製に Error
データ型を
使います。例外は関数を呼ぶことで捕捉し、有益なエラーメッセージを
印字する処理をします。
example12.hs で使えるコード |
---|
-- これはパースエラーを表現する型です data ParseError = Err {location::Int, reason::String} -- これをエラークラスのインスタンスとします instance Error ParseError where noMsg = Err 0 "Parse Error" strMsg s = Err 0 s -- モナド型構築子については、Either ParseError を使います -- これは Left ParserError で失敗を表現し、 -- 型 a の成功した結果は Right a で表現します type ParseMonad = Either ParseError -- parseHexDigit は ParseMonad モナド内で、単一の16進数を -- Integer に変換し、個々の不正文字に対してエラーを投げます parseHexDigit :: Char -> Int -> ParseMonad Integer parseHexDigit c idx = if isHexDigit c then return (toInteger (digitToInt c)) else throwError (Err idx ("Invalid character '" ++ [c] ++ "'")) -- parseHex 16進数の文字列をパースし ParseMonad モナド内で -- Integer に変換します。parseHexDigit からのパースエラーは -- parseHex からの例外復帰を引き起こします。 parseHex :: String -> ParseMonad Integer parseHex s = parseHex' s 0 1 where parseHex' [] val _ = return val parseHex' (c:cs) val idx = do d <- parseHexDigit c idx parseHex' cs ((val * 16) + d) (idx + 1) -- toString は Integer を ParseMonad モナド内で String に変換します toString :: Integer -> ParseMonad String toString n = return $ show n -- convert は16進数文字列を10進文字列に変換します。 -- 入力文字列上でのパースエラーは出力文字列として -- エラーの説明メッセージを生成します。 convert :: String -> String convert s = let (Right str) = do {n <- parseHex s; toString n} `catchError` printError in str where printError e = return $ "At index " ++ (show (location e)) ++ ":" ++ (reason e) |
Prev: Maybe モナド | TOC: 目次 | Next: List モナド |