はじめてのモナド
まず課題1。
亀プログラム処理系プログラムがエラーメッセージを返せないのは、すべてのエラーがMaybeモナドのNothingになってしまい、それ以外の情報を抽出できないため。
一方で、Maybeモナドを亀プログラム処理系プログラムで使用するのは自然。なぜならモナドは、「成功するかどうかわからない処理を連続して行い、失敗した時点でやめる」ためのものだから。これはパース処理にうってつけの動作。
そこで、エラー時のメッセージを保持するdata構造を定義し、Maybeと同様に動作できるようにする。そのためには、MaybeモナドのNothingがエラーメッセージ文字列を保持するようなdata構造であればよい。あまり深く考えず、Maybeモナドを拡張したモナドPMaybeを定義する。
data PMaybe a = PJust a | PErr String deriving Show instance Monad PMaybe where PJust x >>= f = f x PErr s >>= f = PErr s return x = PJust x fail s = PErr s
違いはfailだけなので、モナド則は満たす。
これを使ってパーサを定義する。簡単のため、「go」しかコマンドのないプログラムの簡易パーサを定義してみる。
{- パーサ型クラス -} class TTParser a where ttParse :: a -> PMaybe a {- コンテキストデータ構造 後ろのwordのリストをパースして 前のStringリストに結果を入れていく -} newtype TCntxt = TCntxt ([String], [String]) instance Show TCntxt where show (TCntxt a) = show a {- TCntxtはTTParserのインスタンス -} instance TTParser TCntxt where ttParse (TCntxt (xs, ("go":ys))) = PJust (TCntxt ( ("pGo":xs), ys )) ttParse (TCntxt (xs, [])) = PJust (TCntxt (xs, [])) ttParse (TCntxt (xs, (y:ys))) = PErr ("***ERROR*** [" ++ y ++ "]") {- テストデータ -} testJustCntxt = TCntxt ([], ["go","go"]) testErrCntxt = TCntxt ([], ["go","og"])
実行すると以下のようになる。
*Main> ttParse testJustCntxt PJust (["pGo"],["go"]) *Main> ttParse testJustCntxt >>= ttParse PJust (["pGo","pGo"],[]) *Main> ttParse testJustCntxt >>= ttParse >>= ttParse PJust (["pGo","pGo"],[]) *Main> ttParse testErrCntxt PJust (["pGo"],["og"]) *Main> ttParse testErrCntxt >>= ttParse PErr "***ERROR*** [og]"
注みっつ。