Upload
krdlab
View
421
Download
1
Embed Size (px)
DESCRIPTION
「名前は聞いたことがあるけど,どういうものなの?」という方向けの資料です.
Citation preview
Streaming data processing ライブラリの紹介(主に conduit)
KrdLab(2013/12/21)
はじめに
ときどきコードが出てきますが
「ふーん,こんな感じなのか」
って見てもらえれば幸いです
今回の内容
これは何?
何があるの?
Conduit の紹介
ほんの少しだけ中身をのぞくと…
まとめ
Streaming data processing ライブラリって何?
ストリームデータに対して統一インタフェースを提供 どこから来たデータであっても同じように扱える
リソースの解放をコントロール lazy IO の問題を解決
統一されたインタフェース
ソケット/ファイル/メモリ,どこから来たデータなのかに関係なく扱える データの生成/変換/消費
これらを記述する primitive
これらを連結する operator
ライブラリごとに若干名前が異なりますが… Source/Process
Source/Conduit/Sink
Pipe/Proxy
InputStream/OutputStream
-- だいたいこんな感じ
run $ source (ope) process (ope) ...
ストリームデータの抽象化
リソースの解放をコントロール (1/2)
lazy IO のみで構成すると大きなデータでもコンスタントなメモリで処理できる
しかし,lazy IO はリソース解放のタイミングが難しい GC 任せにすると解放タイミングが非決定的
かといって完璧に管理するのも大変
例えば hGetContents の返す String はすべてのデータを表す えっ!?
引き回しはじめたらどこで閉じたら良いのかわからない…
-- 単純かつ極端な例 (もちろんコンパイルは通る)
main = dos <- withFile “test.txt” ReadMode hGetContentsputStr s -- あっ… 表示されない…
(参考) http://www.haskell.org/haskellwiki/Iteratee_I/O
リソースの解放をコントロール (2/2)
リソースは使い終わったら即時回収 リソースは貴重品
もちろん “constant memory”
“deterministic resource handling”
“resource and exception safety”
main = runResource $sourceFile “test.txt” $$ sinkHandle stdout
有名どころの Implementasions
conduit Yesod の作者が作っている
ResourceT でリソース管理
pipes よく conduit と一緒に取り上げられ,確か conduit の実装に影響を与えていたはず
リソースのあたりは pipes-safe
io-streams 他と少し毛色が違う,IO をベースとした API
リソースのあたりは catch/bracket/with*
あと machines というものもあるよ!
今回のターゲット
conduit について紹介します
machines についてはブログ参照 「Haskell の machines に入門してみた,というお話」
http://krdlab.hatenablog.com/entry/2013/03/16/204039
Conduit: リスト操作
-- List の例
import Data.Conduitimport qualified Data.Conduit.List as CL
main = dol <- CL.sourceList [1..10] $$ CL.map (+1) =$ CL.consumeprint l
-- [2,3,4,5,6,7,8,9,10,11]
Conduit: ファイル操作
宣言的
加工したい場合も宣言的にかける
main = runResourceT $sourceFile “test.txt” $$ sinkFile “output.txt”
main = runResourceT $sourceFile “test.txt”$= decode utf8$= CL.map toUpper$= encode utf8$$ sinkFile “output.txt”
Conduit: Core & Generalized Types
type Source m o データの生産
type Conduit i m o データの変換
type Sink i データの消費
一般化したやつ type Producer m o
type Consumer i m r
(補足:上 3 つと違って input/output が forall)
Conduit: Operators
各コンポーネントをつなぐ
($$) :: Source m a -> Sink a m b -> m b connect
($=) :: Source m a -> Conduit a m b -> Source m b fuse
(=$) :: Conduit a m b -> Sink b m c -> Sink a m c fuse
(=$=) :: Conduit a m b -> ConduitM b c m r -> ConduitM a c m r fuse
Conduit: Primitives
自分で中身を書きたいときに使う
await :: Monad m => Consumer i m (Maybe i) 上流からの入力を待つ
yield :: Monad m => o -> ConduitM i o m () 値を下流へ流す
leftover :: i -> ConduitM i o m () 入力の読み残しを次に渡す
Conduit: Primitives の利用例
リストを加工する例
Conduit: Internal (少しだけです)
Conduit: Internal
data Pipe l i o u m r l: left over (otherwise Void)
i: input values
o: output values
u: upstream からの戻り値
m: ベースモナド (ex. IO)
r: 最終的な戻り値
コンストラクタは 5 種類 HaveOutput/NeedInput/Done/PipeM/Leftover
Conduit: Internal - Core datatype
data ConduitM i o m r i: input values
o: output values
m: ベースモナド (最終的に返されるコンテキスト)
r: 最終的な戻り値
内部には Pipe を持っている newtype ConduitM i o m r = ConduitM { unConduitM :: Pipe i i o () m r }
Conduit: Internal - Types
type Source m o = ConduitM () o m ()
type Conduit i m o = ConduitM i o m ()
type Sink i = ConduitM i Void
type Producer m o = forall i. ConduitM i o m ()
type Consumer i m r = forall o. ConduitM i o m r
すべてが Pipe
Conduit: 要するに
ストリームデータを Pipe 構造に変換
処理を Pipe で記述
演算子で連結
↓
Conduit の処理は Pipe を interpret する
まとめ
良い感じにストリームデータを扱える 統一されたインタフェース
リソース解放のコントロール
いろいろ実装あるけど,今回は conduit を紹介した
良い感じでコンポーネントを書くための primitive も提供されている
内部は Pipe を処理している
すぐに試してみたい人は “FP Haskell Center” を使うと楽です https://www.fpcomplete.com/