6

コード スニペットを見てみましょう。

pSegmentBegin p i   = pIndentExact i *> ((:) <$> p i <*> ((pEOL *> pSegment p i) <|> pure []))

パーサーでこのコードを次のように変更すると:

pSegmentBegin p i   = do
    pIndentExact i
    ((:) <$> p i <*> ((pEOL *> pSegment p i) <|> pure []))

エラーが発生しました:

canot compute minmal length of a parser due to occurrence of a moadic bind, use addLength to override

上記のパーサーは同じように動作するはずだと思いました。なぜこのエラーが発生するのですか?

編集

上記の例は (質問を単純化するために) 非常に単純であり、以下に示すように、ここで do 表記を使用する必要はありませんが、使用したい実際のケースは次のとおりです。

pSegmentBegin p i   = do
    j <- pIndentAtLast i
    (:) <$> p j <*> ((pEOL *> pSegments p j) <|> pure [])

do ステートメントの前に「addLength 1」を追加すると問題が解決することに気付きましたが、正しい解決策かどうかはわかりません。

pSegmentBegin p i   = addLength 2 $ do
    j <- pIndentAtLast i
    (:) <$> p j <*> ((pEOL *> pSegments p j) <|> pure [])
4

3 に答える 3

8

何度も述べてきたように、モナド インターフェイスは可能な限り避けるべきです。アプリケーション インターフェイスが好まれる理由を説明してみましょう。

私のライブラリの特徴の 1 つは、問題を挿入または削除してエラー修正を行うことです。もちろん、ここで無制限の先読みを行うことができますが、それではプロセスが非常に高価になります。そのため、3 つのステップの限定的な先読みのみを行います。

ここで、式を挿入する必要があり、式の選択肢の 1 つが次のとおりであるとします。

expr := "if" expr "then" expr "else" expr

次に、この代替案を選択すると別の式などの挿入が必要になるため、この代替案を除外します。したがって、代替案の抽象的な解釈を実行し、引き分けの場合 (つまり、制限された先読みのコストが等しい場合) に 1 つを取るようにします。非再帰的な選択肢の。

残念ながら、バインドの右側の長さは左側の結果に依存する可能性があるため、モナドパーサーを記述すると、このスキームは機能しなくなります。そのため、エラー メッセージを発行し、プログラマーの助けを借りて、この代替手段が消費するトークンの数を示してもらいます。再帰的で無限の挿入につながる可能性のあるものに有限の長さを指定しない限り、実際の値はそれほど重要ではありません。挿入の場合に最短の代替を選択するためにのみ使用されます。

この抽象的な解釈にはコストがかかります。すべてのパーサーをモナド スタイルで記述すると、この分析が何度も繰り返されることは避けられません。そのため、適用可能な代替手段がある場合は、このライブラリを使用するときにモナディック スタイルのパーサーを記述しないでください。

于 2013-08-17T19:00:02.497 に答える