pipes-parseの概要

## pipes-parseとは

ストリーミングライブラリである pipes の上に構成したパーサを書くためのライブラリ。「入力をパースしてから出力してくれる `Pipe`」ではないことに注意が必要。つまり、 [Data.Conduit.Attoparsec](https://hackage.haskell.org/package/conduit-extra-1.1.9/docs/Data-Conduit-Attoparsec.html) のように `Conduit` になっているわけではないので、同じ感覚で `>->` で間に挟んで使おうとすると面食らうだろう。

## 読むべきドキュメント

pipesが正しいListT相当のモノを提供してますよって前提を理解した上で、[Haskell for allのエントリ](http://www.haskellforall.com/2014/02/pipes-parse-30-lens-based-parsing.html)や[チュートリアル](https://hackage.haskell.org/package/pipes-parse-3.0.2/docs/Pipes-Parse-Tutorial.html)を読む。[yesod webの反応](http://www.yesodweb.com/blog/2014/02/ideas-pipes-parse)も読むのにいい。でもって、[pipes-groupのチュートリアル](https://hackage.haskell.org/package/pipes-group-1.0.2/docs/Pipes-Group-Tutorial.html)も肝になるので読むのを忘れちゃいけない。

## 簡単なまとめ

最初に書いたように `Pipe` の上に別のフレームワークを作っているのが `pipes-parser` であり、それぞれ `Producer`、`Lens`、`Parser`(`StateT`)の3つを使って`Pipe`チックなツールチェインを構成している。それぞれの関係は以下。

* `Producer`と`Lens`を`view`でつなぐと`Producer`になる
* `Lens`と`Parser`を`zoom`でつなぐと`Parser`になる
* `Producer`と`Parser`を`runStateT`でつなぐとパースした結果と残りの要素を含む`Producer`が得られる

まず、なぜ`Parser`が`StateT`かについて。パーザで必要になるのが `peek` であり、これを実現するために `StateT` を使ってる。パースする間は`Producer`を状態として持っておいて、読み込んだ要素を戻すときは`Producer`を変更してやるっていうすごく簡単なアイデア。

でもって、`Lens`の役割。`Producer`とくっつけるときは実は`Lens`である必要がなく、`Getter`で十分である。用は`Producer`の出力をマップできればいいだけ。[pipes-attoparsec](https://hackage.haskell.org/package/pipes-attoparsec-0.5.1.2/docs/Pipes-Attoparsec.html)の`parsed`なんかはこの能力しか持ってない。パース後の構造を文字列に戻してsetする術を知らないので。

`Lens`が活躍するのは`Parser`とくっつけるとき。ストリームに戻された内容を`Lens`のsetterを通して`Producer`へ書き戻すことができる。`Int`を要求するパーサがあるとき、`Producer String`から`Producer Int`を取り出す`Lens`があれば、`Int`が戻されたときに`Lens`のセッターを通して`Producer String`へ要素を戻せるってスンポー。

`Lens`を`Parser`にくっつけるか`Producer`にくっつけるかで何が変わるかは以下のように比較できる。

```hs
mylens :: Lens' (Producer String IO x) (Producer Int IO x)
mylens = iso fw bw
  where
    fw p = do
      lift (next p) >>= \case
        Left r -> return r
        Right (x, p') -> do
          lift $ putStrLn ("forward:" ++ x)
          yield (read x)
          fw p'
    bw p = do
      lift (next p) >>= \case
        Left r -> return r
        Right (x, p') -> do
          lift $ putStrLn ("backward:" ++ show x)
          yield (show x)
          bw p'

myparser :: Parser Int IO (Maybe Int)
myparser = do
  unDraw 256
  peek >>= \case
    Just x -> lift $ putStrLn (show x)
    Nothing -> lift $ putStrLn "Nothing"
  draw

byView :: IO ()
byView = runparser myparser (P.each ["1", "2", "3"] ^. mylens)

byZoom :: IO ()
byZoom = runparser (zoom mylens myparser) (P.each ["1", "2", "3"])

runparser
  :: (Show a, Show b) =>
     StateT (Producer a IO ()) IO b -> Producer a IO () -> IO ()
runparser pa pr = do
  (r, pr') <- runStateT pa pr
  print r
  r' <- evalStateT draw pr'
  print r'
```

```lhs
λ> byView
256
Just 256
forward:1
Just 1
λ> byZoom
256
Just 256
forward:1
backward:1
Just "1"
```

で、このパーザのツールチェーンを `Pipe` と使いたければどうするのかってのが [pipes-groupのチュートリアル](https://hackage.haskell.org/package/pipes-group-1.0.2/docs/Pipes-Group-Tutorial.html) に書いてある。基本的に `Parser` だと一回パースする分はメモリに取るしかないので、このチュートリアルで一番やっちゃダメだって書いてあるコードで使うしかないと思う。

いい感じで使えるのが `Lens` が提供されていてこいつを使いたい場合で、`Producer` をある部分まで `yield` して残りは `return` 値として返すような `Producer` に変換するような `Lens` があるとメモリに貯めずにストリーミングができて、この構造が `Producer` の入れ子になるので `FreeT` モナドが登場してくる。[pipes-text](https://hackage.haskell.org/package/pipes-text-0.0.0.16/docs/Pipes-Text.html)とかは`FreeT`の関数を提供してくれてるので、pipes-groupのチュートリアルにあるようにこいつで `FreeT` の形式でぶった切ってから `concats` でまとめあげるとまた `Producer` に戻ってくれて、非常にストレスなく利用することができる。