Slide 1

Slide 1 text

ストリー ム処理ライブラリはなぜ必要なのか 2015/11/07 藤村大介

Slide 2

Slide 2 text

自己紹介 ● 藤村大介 ● https://twitter.com/ffu_ ● https://github.com/fujimura ● Haskeller/Rubyist

Slide 3

Slide 3 text

この発表について Haskell でストリー ム処理ライブラリが必要とされた背景を説明します。 かつて、 ストリー ムデー タを扱う方法としては、Handle を使う方法、Lazy IO がありました。 しかし、 それぞれに問題があり、 再利用性が高く堅牢なコー ドを書くことは困難でした。 この状況を解決すべく、Conduit、Pipes、io­streams などのストリー ム処理ライブラリが現れ たのですが、 そもそもそれらが必要とされた経緯は具体的にはどのようなものだったのでしょ うか? 既存の方法(Handle、Lazy IO) で起こる問題と、 それがいかに解決されるかを、 改め てここで確認してみようというのがこの発表の趣旨です。 この発表は ​ http://okmij.org/ftp/Haskell/Iteratee/talk­FLOPS.pdf​ をベー スにしています。 ソー スコー ドは ​ https://github.com/fujimura/functional­stream­processing­meetup­sample​ で す。

Slide 4

Slide 4 text

そもそもストリー ムとは? 必要に応じて処理されるデー タの連なりのこと。 具体的には標準入力、 ファイル、 ソケットな ど。 上記の定義はScheme の​ 実装提案書​ より。 この定義では入力側をストリー ムと読んでいます が、 出力側( 標準出力への書き込みなど) もストリー ムとして扱われてる場合が多い気がしま す。 ストリー ムは多くの場合、​ I O ​ です。

Slide 5

Slide 5 text

具体例:200mb のテキストファイルに含まれる空白文字の数を調べる 200mb のファイルをメモリ上に読み込むのは効率的とは言えません。 ストリー ム処理の出番で す。 まずはLazy IO を使う例、Handle を使う例を見てみます。 それぞれのうまくいかない箇所 を確認して、 最後にio­streams を使う例でそれらが解決される様子を見ることにします。

Slide 6

Slide 6 text

1) Lazy IO ● 良いところ ○ 単純でわかりやすい ● 悪いところ ○ 純粋な関数で例外が起こる ■ r e a d F i l e ​ で読んだファイルに不正なバイト列が含まれていた場合は例外が起こり ます 。 そして、Haskell は遅延評価なので、 値は必要になった時に評価され、 例 1 外は評価時に発生します。 つまり、 評価されるまで例外は発生しないのです! こ の性質により、 なんと純粋な関数で例外が起こることがあります。 これのデバッ グは非常に難しいです。 ○ いつ、 どれだけファイルが開かれ、 開放されるか予測しにくい ■ 上記と同じ理由でファイルが開放されるタイミングも単純には予測できません 。 2 大量のファイルが同時に開かれてしまう場合もあります。 1 上記のコー ドの場合、​ L T . f i l t e r ​ で不正なバイト列が評価される時に発生します。 2 ​ s e q ​ や​ B a n g P a t t e r n s ​ を使ってサンクを潰して回る羽目になります。

Slide 7

Slide 7 text

2) Handle ● 良い所 ■ 素朴で理解しやすい ● 悪いところ ○ ロー レベル ■ 素朴さの裏返しですが、Oleg さん言う所の「Haskell で書かれた、 典型的なC の コー ド」 で、 実にロー レベルです。 関数プログラミングとは何だったのか! 3 ○ ロジックを書いている部分より、Handle の世話をしている部分が多い ■ 実際、 ロジックが複雑になると大変なことになります。 コー ド量が増えると、 そ れだけバグの入る可能性も増えます。 3 ​ http://okmij.org/ftp/Haskell/Iteratee/talk­FLOPS.pdf​ の7 ペー ジ参照

Slide 8

Slide 8 text

3) io­streams ● 良い所 ○ ハイレベルでコンポー ザブル ■ ストリー ム自体の扱いはio­streams が隠蔽しています。 ■ ストリー ムを受け取る関数を、Unix のパイプのように ​ > > = ​ で処理を繋ぎます。 繋 がれたそれぞれ処理は抜き差しがしやすいので 、 部品化も簡単です。 4 ○ 安全な例外 ■ それぞれの部品は​ I O ( I n p u t S t r e a m a ) ​ を返すので、​ b r a c k e t ​ で例外を捕捉でき ます。 ○ 少ないメモリ使用量 ■ io­streams が面倒見てくれます。 ● 悪いところ ○ \(^o^)/ ない 4 io­streams で入力を処理する場合、 ​ I n p u t S t r e a m a ‐ > I O ( I n p u t S t r e a m a ) ​ を繋いでいくことになります。

Slide 9

Slide 9 text

まとめ 既存の解決方法には問題がある。 ● Lazy IO: 純粋な関数で例外が起こる、 リソー ス管理が難しい ● Handle: 関数プログラミングっぽくないロー レベルなコー ドになる ストリー ム処理ライブラリで、 それら問題を解決できる。 ● io­streams: サプライズの少ない例外 、 ハイレベルでコンポー ザブル 5 ○ リソー ス管理は自分でやらないといけないけど… ストリー ム処理ライブラリを活用すると、 効率がよく、 見通しがよく、 サプライズが少ないプ ログラムが書けるようになります。 5 実はこれHandle でも​ w i t h F i l e ​ を使えば実現できます。

Slide 10

Slide 10 text

おまけ ● 例外処理 ○ Conduit: ​ resourcet ■ https://www.fpcomplete.com/user/snoyberg/library­documentation/resourcet ○ Pipes: ​ pipes­safe ○ io­streams: ​ bracket​ を使ってくれとのこと

Slide 11

Slide 11 text

参考資料& リンク集 ● io­streams ○ https://hackage.haskell.org/package/io­streams ● Conduit ○ https://hackage.haskell.org/package/conduit ● Pipes ○ https://hackage.haskell.org/package/pipes ● サンプルコー ド ○ https://github.com/fujimura/functional­stream­processing­meetup­sample ● ストリー ム処理ライブラリブー ムの端緒となったIteratee 論文 ○ http://okmij.org/ftp/Haskell/Iteratee/describe.pdf ● Iteratee についての発表資料。 わかりやすい ○ http://okmij.org/ftp/Haskell/Iteratee/talk­FLOPS.pdf ● Iteratee の紹介 ○ https://themonadreader.files.wordpress.com/2010/05/issue16.pdf ○ 和訳: ​ http://d.hatena.ne.jp/tanakh/20100824 ● io­streams リリー スのお知らせ& 紹介。Conduit, Pipes との比較もある ○ http://snapframework.com/blog/2013/03/05/announcing­io­streams ○ Reddit のスレッドが盛り上がった。 面白いです https://www.reddit.com/r/haskell/comments/19qfr6/announcing_first_release_of_iostreams/ ● Conduit, Pipes, io­streams の比較 by Conduit の作者Simon さん ○ https://www.reddit.com/r/haskell/comments/18iskd/upcoming_conduit_10_comparison_to_pipes_iostreams/ ● Lazy IO の罠 ○ http://newartisans.com/2013/05/three­examples­of­problems­with­lazy­io/ ● Conduit 概要 ○ https://www.fpcomplete.com/user/snoyberg/library­documentation/conduit­overview