AttoparsecとMegaparsecについて
先日、HaskellのParserについていろいろ調べた。
きっかけは、attoparsecだとやっぱりエラーメッセージが不親切すぎてデバッグがキツい、ということ。
パーサは所詮純粋関数なので、部品を細かく分けてそれごとに単体テストを書いていけばいいという話もあるが、end-to-endテストがいくつかあれば十分な状況で、デバッグのためにいちいちそれをやるのはキツい。
デバッグ用の情報を得ようにも、attoparsecのParserはmonad transformerではないので、自前で動作ログを残すためのモナドを中に仕込むことはできない。WriterTなどで外から包むことはできるが、そうすると今度はパーサコードに大量のliftが必要になる。
比較的新しいパーサライブラリ。どちらかというとParsecをお手本に、Parsecのイケてないところを徹底的になんとかした、という感じ。
下記、parsersと非互換なのはやむを得ない事情から、らしい。
ReadP, parsec, attoparsecを共通APIで扱えるパーサコンビネータークラス。
エラーメッセージに注力したパーサ。色付きでエラーを出せる、らしい。
Parsecのtryと<|>演算子の使い方について。
Parsecで"try a <|> b"と書くと、aがfailしたらそのエラー情報は丸ごと消えてなくなる。これにより、bもfailした場合に直感に反するエラーメッセージが出たりする。
そもそも問題は、aのパース過程で既にbもfailすると分かっているのに(パース分岐がaに入ると確定しているのに)bを実行することである。そこで、tryをaの中に入れ込むよう、スコープを絞ってしまうのがいい、ということを論じている。Parsecの<|>は左辺が入力を消費せずにfailした場合のみ右辺を実行するので、aが何かしらの入力を消費すればbは実行されない。
ちなみにattoparsecは全てのパーサアクションにtryがついていると同等である(常にbacktrackする)ので、わりと上記のような厄介な状況に出くわすと思う。大きめのParserアクションで<|>を使うと途端にデバッグが難しくなる。どこでエラーになったのか分からないからだ。
attoparsecはエラーの場所に関する情報を出してくれないので、attoparsec-conduitパッケージ(現conduit-extraパッケージ)側でlineとcolumnを数えて場所情報を出せるようにした、という話。
ParsecからMegaparsecへの乗り換えガイド。
Megaparsec 4と5の新機能について。
incremental parseはversion 4でできるようになったが、Megaparsecはincremental parsingをきちんとサポートするものではないとのこと。考えてみれば、incremental parse(というかinfinite stream)をパースしようとしたらpositionとかあんまりキープできないよな。