Control.Monad.Error モジュールを使うと、自前のエラーモナドで例外処理ができるようになるらしい。説明を読んでもよくわからなかったので、all about monads の例をコピーして実験してみた。
まず、次の5行だけを errormonad.hs に作成して、ghci で試した。 import Control.Monad.Error import Data.Char data ParseError = Err {location::Int, reason::String} deriving Show 実行例: *Main> Err 0 "error" Err {location = 0, reason = "error"} つぎに次の2行を付け加えて ParseError を Error クラスのインスタンスにした。noMsg と strMsg を定義するのが大切らしい。 instance Error ParseError where noMsg = Err 0 "Parse Error" strMsg s = Err 0 s 試してみた: *Main> noMsg::ParseError Err {location = 0, reason = "Parse Error"} その次にやったのは、ParseMonad というタイプを定義して ParseError を Either 型でラッピングした。そうすると、関数の型宣言のとき戻り値を ParseMonad a というタイプにしておけば、正常実行のときは Right a が、エラーが起きると Left ParseError が戻される。 type ParseMonad = Either ParseError parseHexDigit :: Char -> Int -> ParseMonad Integer parseHexDigit c idx = if isHexDigit c then return (toInteger (digitToInt c)) else throwError (Err idx ("invalid character '" ++ [c] ++ "'")) 実験した。 *Main> parseHexDigit 'a' 0 Right 10 *Main> parseHexDigit 'x' 0 Left (Err {location = 0, reason = "invalid character 'x'"}) うまく例外が発生した。自前のデータ型のエラー値をもっていると、ほかのエラー型と別に管理ができるので便利だ。instance 宣言で Error クラスのインスタンスにして noMsg と strMsg に登録して、ParseMonad で ParseError を Either 型のモナドでラッピングしてやれば、Right と Left に分けて出力してくれる。意外と簡単で便利な機能だった。例外の発生は throwError を ParseError に関数適用させればいい。しくみはよくわからないがなんとなく使い方がつかめた。 残りの部分を追加した errormonad.hs の全体はつぎのようになる。 import Control.Monad.Error import Data.Char data ParseError = Err {location::Int, reason::String} deriving Show instance Error ParseError where noMsg = Err 0 "Parse Error" strMsg s = Err 0 s type ParseMonad = Either ParseError parseHexDigit :: Char -> Int -> ParseMonad Integer parseHexDigit c idx = if isHexDigit c then return (toInteger (digitToInt c)) else throwError (Err idx ("invalid character '" ++ [c] ++ "'")) 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 n = return $ show n 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) throwError で発生させた例外は catchError で捕捉することができる。catchError の使い方は Prelude の catch とほぼ同じ。 実行例: *Main> convert "3e" "62" *Main> convert "3x" "At index 2:invalid character 'x'" 複雑にみえたエラー・モナドの使い方は、一つずつの部品を試していったらそれほど難解でもなかった。Haskell のいいところは、プログラムが関数なので、部品のテストが容易にできるということだ。これで、ふたたび『48時間で作るScheme』に挑戦する準備ができた。
by tnomura9
| 2011-12-12 06:08
| Haskell
|
Comments(0)
|
カテゴリ
新型コロナウイルス 主インデックス Haskell 記事リスト 圏論記事リスト 考えるということのリスト 考えるということ ラッセルのパラドックス Haskell Prelude Ocaml ボーカロイド 圏論 jQuery デモ HTML Python ツールボックス XAMPP Ruby ubuntu WordPress 脳の話 話のネタ リンク 幸福論 キリスト教 心の話 メモ 電子カルテ Dojo JavaScript C# NetWalker ed と sed HTML Raspberry Pi C 言語 命題論理 以前の記事
最新のトラックバック
最新のコメント
ファン
記事ランキング
ブログジャンル
画像一覧
|
ファン申請 |
||