モナドではなくアプリケーションとしてパーセクを使うべきだというコンセンサスがあるようです。モナディック構文解析よりも適用構文解析の利点は何ですか?
モナディック構文解析は行われていますか?
モナディック解析とアプリケーション解析の主な違いは、逐次合成の処理方法にあります。アプリケーションパーサーの場合、(<*>)
を使用しますが、モナドでは(>>=)
を使用します。
(<*>) :: Parser (a -> b) -> Parser a -> Parser b
(>>=) :: Parser a -> (a -> Parser b) -> Parser b
モナディックアプローチは、2番目のパートの文法が最初のパートの結果に依存できるため、より柔軟ですが、実際にはこの追加の柔軟性はほとんど必要ありません。
柔軟性を高めても害はないと考えるかもしれませんが、実際には害を及ぼします。パーサーを実行せずに有用な静的分析を行うことができなくなります。たとえば、パーサーが空の文字列と一致できるかどうか、および一致する最初の文字が何であるかを知りたいとします。機能が欲しい
empty :: Parser a -> Bool
first :: Parser a -> Set Char
アプリケーションパーサーを使用すると、この質問に簡単に答えることができます。 (私はここで少し浮気しています。候補のパーサー「言語」に(<*>)
および(>>=)
に対応するデータコンストラクターがあると想像してください)。
empty (f <*> x) = empty f && empty x
first (f <*> x) | empty f = first f `union` first x
| otherwise = first f
ただし、モナディックパーサーでは、入力を知らなければ、2番目の部分の文法が何であるかがわかりません。
empty (x >>= f) = empty x && empty (f ???)
first (x >>= f) | empty x = first x `union` first (f ???)
| otherwise = first x
より多くを許可することで、私たちはより少ない推論をすることができます。これは、動的型システムと静的型システムの間の選択に似ています。
しかし、これの意味は何ですか?この余分な静的知識を何のために使用するのでしょうか?たとえば、次の文字を各代替のfirst
セットと比較することにより、LL(1)解析でのバックトラックを回避するために、たとえばそれを使用できます。また、2つの選択肢のfirst
セットが重複しているかどうかを確認することで、これが曖昧かどうかを静的に判断することもできます。
別の例は、S。Doaitse SwierstraとLuc Duponcheelによる論文 Deterministic、Error-Correcting Combinator Parsers に示されているように、エラー回復に使用できることです。
ただし、通常、適用構文解析とモナド構文解析のどちらを選択するかは、使用している構文解析ライブラリの作成者によってすでに行われています。 Parsecなどのライブラリが両方のインターフェースを公開する場合、どちらを使用するかの選択は純粋にスタイル上のものです。場合によっては、モナディックコードよりもアプリケーションコードの方が読みやすく、逆の場合もあります。
パーサーが純粋に適用可能であれば、その構造を分析し、実行する前に「最適化」することが可能です。パーサーがモナドである場合、それは基本的にチューリング完全なプログラムであり、興味深い分析をほとんどすべて実行することは、停止問題を解決することと同じです(つまり、不可能です)。
ああ、そうです、スタイルにも違いがあります...
モナディックパーサーよりもアプリケーションパーサーを好む主な理由は、どのような状況でもモナディックコードよりもアプリケーションコードを好む主な理由と同じです。
これは、より一般的なエンジニアリングの口論の例です:仕事を成し遂げる最も簡単なツールを使用します。台車が行うときにフォークリフトを使用しないでください。クーポンをカットするためにテーブルソーを使用しないでください。純粋な場合は、IO
にコードを記述しないでください。複雑にしないでおく。
しかし、時々、あなたはMonad
の追加の能力を必要とします。これの確かな兆候は、これまでに計算された内容に基づいて計算のコースを変更する必要がある場合です。解析用語では、これは、これまでに解析されたものに基づいて、次に来るものを解析する方法を決定することを意味します。つまり、この方法で状況依存文法を構築できます。
Parsecを使用すると、Applicativeを使用する利点は単なるスタイルです。モナドには、より強力であるという利点があります。状況依存パーサーを実装できます。
Doaitse SwierstraのUU解析は、アプリケーションのみで使用するとより効率的です。
モナドはApplicativesより厳密により機能的な抽象化です。あなたは書くことができます
instance (Monad m) => Applicative m where
pure = return
(<*>) = ap
しかし、書く方法はありません
instance (Applicative a) => Monad a where
return = pure
(>>=) = ???
つまり、本質的にはstyleの問題です。 Applicativeはモナドよりも厳密に小さいインターフェースであるため、return
とap
を使用しているとしたら、pure
と<*>
を使用してもパフォーマンスの低下なしになるはずです。<*>
はap
よりも高度に最適化できる場合があります。 (しかし、賢いGHC書き換えルールを使用すると、関係なく同じ最適化を達成できることがよくあります。)
モナディック構文解析は行われていますか?
モナドはApplicativeのサブセットなので、Applicative解析はモナディック解析のサブセットであると私は結論づけます。