Conduitの使い方を調べていました。 公式のREADMEがすさまじく分かりやすかったので、これを読めばOKでした。 以前のConduitに関する記事などで見られた($=),(=$),(=$=),($$)といったオペレータは、(.|)に統一されたようで、使いやすくなっていました。 どうやら2016年9月に移行したようですね。 いくらか戸惑った点を記しておきます。

インポートすべきライブラリが変わっている

過去の記事などでは、Conduitの利用者は、依存するライブラリ、importを以下のように設定すればよかったですが、

dependencies:
  - conduit
  - conduit-extra # 任意
import           Data.Conduit              -- conduit - 必須
import qualified Data.Conduit.List as CL   -- conduit - 任意
import qualified Data.Conduit.Binary as CB -- conduit-extra - 任意

移行後はconduitではなく、conduit-combinatorsを指定します。

dependencies:
  - conduit-combinators
  - conduit-extra # 任意
import           Conduit                   -- conduit-combinators - 必須
import qualified Data.Conduit.Binary as CB -- conduit-extra - 任意

runResourceTは使わなくてよい

runConduitResを使えばいいです。これの実装は単純にrunResourceT . runConduitとなっています。

練習

ファイルをソースにして練習

公式ドキュメントを読んだ後、ファイルを行ごとに処理するコードを書いてみた。

{-# LANGUAGE OverloadedStrings #-}

import           Conduit
import qualified Data.Conduit.Binary as CB
import           Data.Monoid         ((<>))
import qualified Data.Text           as T
import qualified Data.Text.IO        as T

main :: IO ()
main =
  runConduitRes
    $ sourceFile "input.txt"
   .| CB.lines
   .| decodeUtf8C
   .| mapC (<> "!")
   .| mapM_C (liftIO . T.putStrLn)

結果

入力ファイル input.txt

あ
い
う
え
お

標準出力

あ!
い!
う!
え!
お!

Stateモナドと組み合わせるテスト

import           Control.Monad.State -- 追加

main :: IO ()
main = do
  (n,t) <- flip runStateT "" $
    runConduitRes
      $ sourceFile "input.txt"
     .| CB.lines
     .| decodeUtf8C
     .| awaitForever act
     .| lengthCE
  print n
  T.putStrLn t
  where
    act t = do
      let t' = t <> "!"
      modify (t' <>)
      yield t'

結果

10
お!え!う!い!あ!