Data.Conduit

  • 1
    いいね
  • 1
    コメント
この記事は最終更新日から1年以上が経過しています。

噂のconduit。今回は標準入力触るスクリプトにしたのでconduit.hsみたいな感じで以下を保存。標準入力への入力を大文字にするだけ。.の入力で終了する。

module Main (main) where
import Data.Char (ord, chr, toUpper)
import Data.Conduit
import qualified Data.Conduit.Binary as B
import qualified Data.Conduit.Util as U
import qualified Data.ByteString as BS
import Data.Word (Word8)
import System.IO (stdin, stdout)

main :: IO ()
main = runResourceT $ do
  B.sourceHandle stdin
    $= B.takeWhile (\c -> c /= word8 '.')
    $= filterUc
    $$ B.sinkHandle stdout
  return ()
  where
    word8 = fromIntegral . ord

filterUc :: Monad m => Conduit BS.ByteString m BS.ByteString
filterUc = U.conduitState
           ()
           (\() str -> return $ U.StateProducing () [ucString str])
           (\() -> return [])
  where
    uc :: Word8 -> Word8
    uc = fromIntegral . ord . toUpper . chr . fromIntegral
    ucString :: BS.ByteString -> BS.ByteString
    ucString = BS.map uc

SourceSink$$で合体させると処理が走り、何らかの値を出力する。ここではsourceHandlesinkHandleを使ってHandleベースのごく自然なSourceSinkを作って動かしてる。mは任意のモナドでいいのだけど、IO扱う時はResourceTを要求される。Conduitがフィルタ的なもので、SourceSinkの間に好きなだけ挟める。今回みたいにmIOをベースとしたモナドなら色々できるはず。

Conduitを間に挟む場合は*nixの|ほど単純ではなく、型の都合で$==$=$=を使い分けないとならない。とはいえ、Source $= Conduit == Source、Conduit =$ Sink == Sink、Conduit =$= Conduit == Conduit ってだけだから、ま、そんな苦じゃないはず(=のつく方にConduit(導管)を置くって覚えればいいだけ)。

一応実行してみると以下の通り。

% runhaskell conduit.hs
abcdefg
ABCDEFG
This is Haskell.
THIS IS HASKELL

自分でconduit定義する時はstateConduitを使うのが常套手段なのかな。あと、複数個のストリーム扱う時はどうすんだろ。