3

MonadErrorをParsecと一緒に使用しようとしています。私は次のコードスニペットを思いついた:

f5 = do
    char 'a'
    throwError "SomeError"

f6 = f5 `catchError` (\e -> unexpected $ "Got the error: " ++ e)

ret = runErrorT (runParserT f6 () "stdin" "a")

ただし、 catchErrorretLeft "SomeError"効果がないようです。ここでMonadErrorを使用する正しい方法は何ですか?

たとえば、次の場合のように、Parsec自体のエラー処理よりもMonadErrorを使用したいと思います。

try (many1 parser1) <|> parser2

ここでparser1が失敗した場合、parser2は続行されますが、解析を完全に中止する例外があります。

4

3 に答える 3

5

私はあなたが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 StringIdentityEither StringMonadErrorthrowErrorcatchErrorcatchError

質問のサンプルコードに戻ります。それは少し違うことをします。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ので、もちろんそれはパーサーになります...そして私はこれを打ち出すのに役立つ何かにこれを打ち込む方法がわかりません解析プロセス。

于 2010-02-02T18:43:12.643 に答える
2

トラブルシューティングのためにパーサーをデバッグしようとしている場合は、、、などを使用する方がおそらく簡単errorですDebug.Trace

一方、実際のプログラムの一部として一部の入力の解析を終了する必要があるが、try (...) <|>構造が原因で終了しない場合は、ロジックにバグがあるため、文法を停止して再考する必要があります。エラー処理でそれをハックするよりも。

パーサーを特定の入力で終了させたいが、他の入力では終了させたくない場合は、入力ストリームに何かが欠落しているか(追加する必要があります)、パーサーが問題の解決策ではありません。

パーセクが致命的でないエラーから正常に回復し、可能な場合は試行を続け、続行できない場合はエラーで終了する場合は、パーセク以外のものを検討することをお勧めします。これは実際には設計されていないためです。そのために。ユトレヒト大学のHaskellパーサーコンビネーターライブラリは、この種のロジックをはるかに簡単にサポートしていると思います。

編集:Parsec自体がMonadErrorgoesのインスタンスである限り、そうです。独自のエラー処理にはその機能が含まれています。あなたがやろうとしているのは、Parsecの上に2番目のエラーモナドをスタックすることです。そのように「冗長」なモナド変換子を区別するのは一般的に厄介なので、おそらく問題があります。複数の州のモナドを扱うことは、より有名に厄介です。そのため、Parsec(州のモナドも同様)は、カスタム状態を保持する機能を提供します。

言い換えれば、Parsecがエラーモナドであることはまったく役に立ちません。実際、問題をより困難にするという意味で、実際にはほとんど関係があります。

于 2010-02-02T20:30:25.720 に答える
2

実際のプログラムの一部として一部の入力の解析を終了する必要があるが、try(...)<|>構文が原因で終了しない場合は、ロジックにバグがあるため、停止して再考する必要があります。エラー処理で文法をハックするのではなく、文法。

パーサーを特定の入力で終了させたいが、他の入力では終了させたくない場合は、入力ストリームに何かが欠落しているか(追加する必要があります)、パーサーが問題の解決策ではありません。

この答えは、問題が文法にあるという仮定に基づいています。しかし、文法を使用してコンパイラーにフィードしている場合、文法で処理できない他のエラーがあります。定義されていない変数への変数参照を考えてみましょう。また、言語はシングルパスとして指定され、変数は検出されたものとして評価されます。そうすれば、文法は問題ありません。解析は問題ありません。しかし、文法で指定されたものを評価した結果、エラーが発生しました。既存の「失敗」または「予期しない」、またはこの問題に対処するには不十分です。より高いレベルのエラー処理に頼ることなく、解析を中止する手段があると便利です。

于 2010-04-23T17:55:53.567 に答える