| 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 モナド |