Parsec で MonadError を使用する
-
25-09-2019 - |
質問
MonadError を Parsec と一緒に使用しようとしています。次のコードスニペットを思いつきました。
f5 = do
char 'a'
throwError "SomeError"
f6 = f5 `catchError` (\e -> unexpected $ "Got the error: " ++ e)
ret = runErrorT (runParserT f6 () "stdin" "a")
しかし、 ret
は Left "SomeError"
, 、catchErrorは何の効果もないようです。ここで MonadError を使用する正しい方法は何ですか?
たとえば次のような場合、Parsec 独自のエラー処理よりも MonadError を使用することを好みます。
try (many1 parser1) <|> parser2
ここで parser1 が失敗した場合、parser2 は続行しますが、解析を完全に中止する例外が必要です。
解決
巻き込もうとしているような印象を受ける MonadError
間違った理由で。
の中に try (many1 parser1) <|> parser2
, 、あなたが避けようとしている行動は、 try
そして <|>
-- それが気に入らない場合は、別のコンビネータを使用してください。おそらく次のような表現でしょう (many1 parser1) >> parser2
あなたにとってもっとうまくいくでしょうか?(これにより、次の結果が破棄されます) (many1 parser1)
;もちろん使用できます >>=
からの結果を結合します (many1 parser1)
からの人たちと parser2
.)
(注記:この時点より下では、目前の問題に対する実際に優れた解決策はなく、いくつかのことがおそらく機能しない理由についていくつか考えているだけです...これが(ある程度)啓発になることを願っていますが、あまり期待しないでください。)
ParsecT と MonadError の相互作用を詳しく調べます。申し訳ありませんが、それは少し面倒で、OP がやりたいことを実行する最善の方法はまだよくわかりませんが、以下の内容が少なくとも成功しない理由についての洞察を提供することを願っていますオリジナルのアプローチ。
まず、注意してください Parsec が MonadError のインスタンスであるというのは正しくありません. 。Parsec は、内部モナドが Identity である場合に ParsecT によって生成されるモナドです。ParsecT は、それ自体が動作する MonadError のインスタンスである内部モナドが与えられた場合に限り、MonadError のインスタンスを生成します。GHCi 相互作用の関連する断片:
> :i Parsec
type Parsec s u = ParsecT s u Identity
-- Defined in Text.Parsec.Prim
-- no MonadError instance
instance (MonadError e m) => MonadError e (ParsecT s u m)
-- Defined in Text.Parsec.Prim
-- this explains why the above is the case
-- (a ParsecT-created monad will only become an instance of MonadError through
-- this instance, unless of course the user provides a custom declaration)
次、 catchError と ParsecT を使った実際の例を作ってみましょう. 。次の GHCi 相互作用を考えてみましょう。
> (runParserT (char 'a' >> throwError "some error") () "asdf" "a" :: Either String (Either ParseError Char)) `catchError` (\e -> Right . Right $ 'z')
Right (Right 'z')
型の注釈は必要であるようです (これは私にとって直感的に理解できるように思えますが、元の質問とは関係ないので、詳しく説明しません)。式全体の型は GHC によって次のように決定されます。
Either String (Either ParseError Char)
これで、通常の解析結果が得られました -- Either ParseError Char
-- で包まれています Either String
通常の代わりにモナド Identity
モナド。以来 Either String
の例です MonadError
, 、使用できます throwError
/ catchError
, 、しかしハンドラーはに渡されました catchError
もちろん、正しい型の値を生成する必要があります。残念ながら、これは解析ルーチンから抜け出すにはあまり役に立ちません。
質問のコード例に戻ります。 それは少し異なることをします。種類を調べてみましょう ret
質問で定義されているように:
forall (m :: * -> *) a.
(Monad m) =>
m (Either [Char] (Either ParseError a))
(GHCiによると...単型性の制限を解除する必要があることに注意してください。 {-# LANGUAGE NoMonomorphismRestriction #-}
型注釈なしでコードをコンパイルします。)
そのタイプは、何か面白いことができる可能性についてのヒントです ret
. 。さぁ行こう:
> runParserT ret () "asdf" "a"
Right (Left "some error")
今にして思えば、与えられたハンドラーは catchError
を使用して値を生成します unexpected
, もちろん、それはパーサー (として使用可能) になります...残念ながら、これを解析プロセスから抜け出すのに役立つ何かに組み込む方法がわかりません。
他のヒント
あなたがトラブルシューティングするために、パーサーをデバッグしようとしている場合は、それが使用error
、Debug.Trace
、またはその他もろもろに、おそらく簡単です。
try (...) <|>
構築物のそういない場合は、一方で、あなたはあなたのロジックのバグを持っています>あなたは停止し、エラー処理とその周りにあなたの文法ではなく、ハックを再考する必要があります。
あなたはパーサが他人与えられた入力に時間の一部を終了しますが、しないようにしたい場合は、、その後、何かがあなたの入力ストリームから欠落している(して、コメントを追加しなければならない)、またはパーサはあなたの問題を解決されていませんか。
あなたは、パーサーが致命的でないエラーから正常に回復し、可能な場合は努力を続けるが、それは続行できませんエラーで終了したい場合は、それはだから、、あなたは...、Parsecの以外のものを検討する必要があります本当にそのために設計されていません。私はユトレヒト大学のHaskellのパーサコンビネータライブラリのサポートは、はるかに簡単にロジックの一種と考えています。
編集:これまでParsecのはMonadError
のインスタンス自体であることが経つにつれてとして - 機能することをはい、と独自のエラー処理包摂。あなたはParsecのの上にあり、スタックA の2番目ののエラーモナドをやろうとしていて、それがそのように、「冗長」ですモナド変圧器を区別するために、一般的に厄介なので、おそらく問題を抱えています。複数の状態モナドに対処することはParsecの(国が同様モナド)理由で保留カスタム状態に機能を提供し、より多くの有名厄介です。
言い換えれば、Parsecのは、エラーモナドであることは、すべてであなたを助けていない、実際にはほとんどがあなたの問題をより困難にするという意味で関連している。
あなたはあなたの実際のプログラムの一環として、いくつかの入力の解析を終了する必要があるが、それがために試してみるとそうでないなら(...)<|>コンストラクトは、あなたはあなたのロジックにバグを持っていますあなたはむしろエラー処理とその周りハックよりも、あなたの文法を停止し、再考する必要があります。の
のパーサーが他人与えられた入力に時間の一部を終了しますが、しないようにしたい場合は、どちらか何かがあなたの入力ストリームから欠落している(して、コメントを追加しなければならない)、またはパーサはあなたの解決策ではありません問題。の
この答えは、文法上の問題があるという仮定に基づいています。私はコンパイラを養うために文法を使用している場合でも、文法は処理できない他のエラーがあります。さんが定義されていない変数への変数参照を、言ってみましょう。そして、言語は、単一のパスとして指定されている、と遭遇したとして変数が評価されます。その後、文法だけで結構です。解析だけで結構です。しかし、エラーが発生した文法で指定されたものを評価した結果、この問題に対処するために、既存の「失敗」または「予期しない」または不十分。それは、より高いレベルのエラー処理に頼らずに解析を中止する手段を持っていいだろう。