Created
February 15, 2012 10:23
-
-
Save lyricallogical/1834901 to your computer and use it in GitHub Desktop.
rpscala69
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
!SLIDE | |
sclaz7 の scalaz.Iteratee の紹介 | |
================================ | |
!SLIDE | |
発端 | |
---- | |
> @halcat0x15a: @lyrical_logical 7のiterateeが別物になってて読めないです・・・・ 11:38 PM - 14 Feb 12 | |
[tweet](https://twitter.com/#!/halcat0x15a/status/169430205029552129) | |
!SLIDE | |
そういうわけなので | |
------------------ | |
読みました | |
!SLIDE | |
Iteratee とは | |
------------- | |
* Haskell の Lazy IO に替わる新しい入力処理として提案された手法 | |
* 二年弱前に Oleg 先生(エライ)が提案してから、様々なライブラリが登場 | |
* 正に Iteratee 戦国時代…! | |
* 現在進行形で改良が続けられている | |
!SLIDE | |
既存の入力処理の問題点 | |
---------------------- | |
* Haskell の Lazy IO がオワコン | |
* 基本的に副作用や例外は遅延評価と相性が悪い | |
* 入出力は副作用や例外を伴う | |
* ヤバイ! | |
* Scala には関係ない | |
* composable ではない | |
* 入力の供給と処理が強く結びついている | |
* etc... | |
!SLIDE | |
入力処理の現実 | |
-------------- | |
* 入力とは、一度に供給されるものではない | |
* man read(2) | |
* ファイル入出力、ネットワークストリーム、etc... | |
* 効率のためには、少しずつ供給される入力を、少しずつ処理しなければならない | |
* バッファリング、暗号、圧縮、エンコーディング等の前処理もある | |
* 大変だ! | |
!SLIDE | |
Design Goal | |
----------- | |
* 速度、メモリ使用量に優れ | |
* 安全であり | |
* 記述力が高い入力処理を実現する | |
!SLIDE | |
そんなことできるの? | |
------------------- | |
どうもできるらしい | |
!SLIDE | |
Scala 界隈における Iteratee の現状 | |
---------------------------------- | |
* Iteratee を導入しているライブラリがいくつか | |
* scalaz | |
* scala-query | |
* Akka | |
* Play 2.0 | |
* scalaz7(new!) | |
* 他にもあったら教えてください | |
!SLIDE | |
Scala と Iteratee の相性 | |
------------------------ | |
* そもそも Haskell のためのもの。Scala との相性は…? | |
* 性能測定などしてないので分かりません | |
* 素朴な移植では恐らく駄目 | |
* Scala なりの設計が多分あるはず | |
* scalaz7 はどうかな? | |
* これ以上は考えないことにしよう | |
!SLIDE | |
というわけで | |
------------ | |
本題に入ります | |
!SLIDE | |
アジェンダみたいな何か | |
---------------------- | |
* 何かイメージ | |
* 入力とは | |
* 入力処理とは | |
* scalaz.Iteratee | |
* scalaz.Iteratee.Input | |
* scalaz.Iteratee.StepT | |
* scalaz.Iteratee.IterateeT | |
* scalaz.Iteratee.EnumeratorT | |
!SLIDE | |
入力 | |
---- | |
* そもそも入力とは | |
* 何らかの値(の列) | |
!SLIDE | |
イメージせよ! | |
------------- | |
 | |
Hello | |
!SLIDE | |
入力の処理 | |
---------- | |
* 入力を取り、何らかの結果を返す | |
!SLIDE | |
イメージせよ! | |
------------- | |
 | |
食べるぜー | |
!SLIDE | |
入力の処理 問題その 1 | |
--------------------- | |
* 入力を取り、何らかの結果を返す | |
* 入力は少しずつ供給される | |
* 処理に必要な分の入力が供給されなかったら? | |
!SLIDE | |
イメージせよ! | |
------------- | |
 | |
もっと食べたいよぉ | |
!SLIDE | |
入力の処理 問題その 1 解決 | |
-------------------------- | |
* 入力を取り、何らかの結果を返す | |
* 入力は少しずつ供給される | |
* 足りなかったら、もっと入力を要求しよう! | |
!SLIDE | |
イメージせよ! | |
------------- | |
 | |
おかわりよこせ | |
!SLIDE | |
入力 問題その 1 | |
--------------------- | |
* そもそも入力とは | |
* 何らかの値(の列) | |
* 一時的になくなってしまうことも | |
* しばらく待てばまた値を供給できる | |
!SLIDE | |
イメージせよ! | |
------------- | |
 | |
エンプティ | |
!SLIDE | |
入力 問題その 1 解決 | |
-------------------------- | |
* そもそも入力とは | |
* 何らかの値(の列) | |
* 一時的になくなってしまうことも | |
* 空を表現可能にしよう | |
!SLIDE | |
イメージせよ! | |
------------- | |
 | |
また食べられるぞ | |
!SLIDE | |
入力 問題その 2 | |
--------------------- | |
* そもそも入力とは | |
* 何らかの値(の列) | |
* 一時的になくなってしまうことも | |
* 空を表現可能にしよう | |
* いつかはなくなってしまうことも | |
* もう値を供給できない | |
!SLIDE | |
イメージせよ! | |
------------- | |
 | |
売り切れです | |
!SLIDE | |
入力 問題その 2 解決 | |
-------------------------- | |
* そもそも入力とは | |
* 何らかの値(の列) | |
* 一時的になくなってしまうことも | |
* 空を表現可能にしよう | |
* いつかはなくなってしまうことも | |
* 終端を表現可能にしよう | |
!SLIDE | |
イメージせよ! | |
------------- | |
 | |
Hello! | |
!SLIDE | |
入力の処理 問題その 2 | |
--------------------- | |
* 入力を取り、何らかの結果を返す | |
* 入力は少しずつ供給される | |
* 足りなかったら、もっと入力を要求しよう! | |
* 時には予期せぬ入力も | |
!SLIDE | |
イメージせよ! | |
------------- | |
 | |
Bad Apple | |
!SLIDE | |
入力の処理 問題その 2 解決 | |
-------------------------- | |
* 入力を取り、何らかの結果を返す | |
* 入力は少しずつ供給される | |
* 足りなかったら、もっと入力を要求しよう! | |
* 時には予期せぬ入力も | |
* エラーを返せるようにしよう | |
!SLIDE | |
イメージせよ! | |
------------- | |
 | |
変なもの食べさせないで! | |
!SLIDE | |
ここまでまとめ | |
-------------- | |
* 入力とは | |
* 何らかの値(の列)か | |
* 空か | |
* 終端である | |
* 入力処理とは、入力を取り | |
* 入力が十分なら何らかの結果を返し | |
* 足りなければ更に要求し | |
* 予期せぬ入力にはエラーを返すもの | |
!SLIDE | |
説明し忘れてたことがあった | |
-------------------------- | |
* ぐだぐだですいません… | |
!SLIDE | |
入力の供給 | |
---------- | |
* 入力処理を行う人は、自分では入力を取ってこれない | |
* 誰かが供給してあげる必要がある | |
* 「おなかいっぱい」か「こんなの食べられない」というまで食べさせ続ける | |
!SLIDE | |
scalaz.Iteratee | |
------------- | |
* scalaz7 の Iteratee 実装 | |
* 主な登場人物 | |
* scalaz.Iteratee.Input | |
* scalaz.Iteratee.StepT | |
* scalaz.Iteratee.IterateeT | |
* scalaz.Iteratee.EnumeratorT | |
!SLIDE | |
scalaz.Iteratee | |
------------- | |
* scalaz7 の Iteratee 実装 | |
* 主な登場人物 | |
* scalaz.Iteratee.Input | |
* 入力に相当 | |
* scalaz.Iteratee.StepT | |
* 入力処理を行う人に相当 | |
* scalaz.Iteratee.IterateeT | |
* 「あるコンテキストで」入力処理を行う人に相当 | |
* scalaz.Iteratee.EnumeratorT | |
* 入力を供給する人に相当 | |
!SLIDE | |
コードを読み始める前に | |
---------------------- | |
* scala で「A もしくは B もしくは C」のような型を定義するには? | |
!SLIDE | |
普通はこう | |
---------- | |
```scala | |
sealed trait Nanika | |
case class A(a : TA) extends Nanika | |
case class B(b : TB) extends Nanika | |
case class C(c : TC) extends Nanika | |
``` | |
sealed + case class/object で ADT | |
!SLIDE | |
コードを読み始める前に | |
---------------------- | |
* scala で「A もしくは B もしくは C」のような型を定義するには? | |
* その型の値を処理するには? | |
!SLIDE | |
普通はこう | |
---------- | |
```scala | |
nanika match { | |
case A(a) => ... | |
case B(a) => ... | |
case C(c) => ... | |
} | |
``` | |
パターンマッチ | |
!SLIDE | |
ところがどっこい | |
---------------- | |
* scala で「A もしくは B もしくは C」のような型を定義するには? | |
* その型の値を処理するには? | |
* これらを scalaz では? | |
!SLIDE | |
こうなる | |
-------- | |
```scala | |
sealed trait Nanika { | |
def fold[Z](caseA : TA => Z, caseB : TB => Z, caseC : TC => Z) | |
} | |
``` | |
ん??? | |
!SLIDE | |
どうしてこんなことに | |
-------------------- | |
* 以下は全て推測ですが… | |
* match は isInstanceOf とか使うので遅い | |
* 分岐のためのメソッドを用意しておけばよい | |
* case class/object と遅延評価は相性が悪い | |
* パラメタに lazy val も by name parameter も使えない | |
!SLIDE | |
兎に角 | |
------ | |
* scalaz ではそんな感じで書かれてる | |
* 読みなれてないと辛い | |
* 脳内で ADT + パターンマッチに変換しましょう | |
!SLIDE | |
というわけで | |
------------ | |
* 読み始めます | |
!SLIDE | |
scalaz.Iteratee.Input | |
--------------------- | |
```scala | |
sealed trait Input[E] { | |
def fold[Z](empty: => Z, el: (=> E) => Z, eof: => Z): Z | |
} | |
``` | |
* E 型の値(の列)の入力を表すトレイト | |
* 早速例の fold です | |
* 空と、何らかの入力と、終端に対するパターンマッチの実装だと思いましょう | |
!SLIDE | |
Input の値達 | |
---------------------------- | |
```scala | |
object Empty { | |
def apply[E]: Input[E] = new Input[E] { | |
def fold[Z](empty: => Z, el: (=> E) => Z, eof: => Z) = empty | |
} | |
} | |
object Element { | |
def apply[E](e: => E): Input[E] = new Input[E] { | |
def fold[Z](empty: => Z, el: (=> E) => Z, eof: => Z) = el(e) | |
} | |
} | |
object Eof { | |
def apply[E]: Input[E] = new Input[E] { | |
def fold[Z](empty: => Z, el: (=> E) => Z, eof: => Z) = eof | |
} | |
} | |
``` | |
みたまま | |
!SLIDE | |
Input のメソッド例 | |
-------------------- | |
```scala | |
def isEof: Boolean = fold(false, // 空の場合 | |
_ => false, // 値がある場合 | |
true) // 終端の場合 | |
``` | |
!SLIDE | |
scalaz.Iteratee.StepT | |
--------------------- | |
```scala | |
sealed trait StepT[X, E, F[_], A] { | |
def fold[Z]( | |
cont: (Input[E] => IterateeT[X, E, F, A]) => Z | |
, done: (=> A, => Input[E]) => Z | |
, err: (=> X) => Z | |
): Z | |
} | |
``` | |
* X はエラー型 | |
* E は Input に渡す、入力の値型 | |
* F はコンテキスト | |
* コンテキストとは何かだって?難しいことは後です! | |
* A は処理の結果の型 | |
!SLIDE | |
StepT の値達 | |
------------ | |
* 重要なのは IterateeT のほう | |
* Input と似たようなものなので飛ばします | |
!SLIDE | |
scalaz.Iteratee.IterateeT | |
------------------------- | |
```scala | |
sealed trait IterateeT[X, E, F[_], A] { | |
def value: F[StepT[X, E, F, A]] | |
def foldT[Z]( | |
cont: (Input[E] => IterateeT[X, E, F, A]) => F[Z] | |
, done: (=> A, => Input[E]) => F[Z] | |
, err: (=> X) => F[Z] | |
)(implicit F: Bind[F]): F[Z] = | |
F.bind(value)((s: StepT[X, E, F, A]) => s(cont, done, err)) | |
} | |
``` | |
* 入力を食べたりおなかいっぱいになったり駄々をこねたりする人 | |
* 型パラメタは StepT と同じ | |
* あるコンテキスト F 上で StepT を扱うのが IterateeT | |
* 各パラメタの意味は次頁 | |
* value て名前は酷くないか…step でいいじゃん | |
!SLIDE | |
IterateeT のメソッド例 | |
------------------------ | |
* ここでコードを紹介できるくらい実装が簡単なものがなかった! | |
* run, apply メソッド | |
* EOF を供給し、処理を終了させ、結果を取り出す | |
* あとエラーハンドリングもする。 | |
* runOrZero メソッド | |
* F[A] が Monoid である場合にのみ利用できる。エラーの時は単位元を返す run | |
* map, flatMap メソッド | |
* おなじみのメソッド。でも実装は少しだけややこしい。 | |
* 基本的に処理が終了するまで引数に渡された関数の適用を遅延する、といえばよいか | |
* contramap メソッド | |
* 後で読む(これはスライドだぞ!何を言っているんだ!) | |
* etc... | |
!SLIDE | |
IterateeT の値達 コンストラクタ編 | |
--------------------------------- | |
```scala | |
def cont[X, E, F[_] : Pointed, A](c: Input[E] => IterateeT[X, E, F, A]): IterateeT[X, E, F, A] = | |
StepT.scont(c).pointI | |
def done[X, E, F[_] : Pointed, A](d: => A, r: => Input[E]): IterateeT[X, E, F, A] = | |
StepT.sdone(d, r).pointI | |
def err[X, E, F[_] : Pointed, A](e: => X): IterateeT[X, E, F, A] = | |
StepT.serr(e).pointI | |
``` | |
* pointI は StepT を IterateeT に変換するメソッド | |
* cont | |
* まだまだ食べるよの人 | |
* 処理が残ってる状態 | |
* done | |
* もうおなかいっぱいだよの人 | |
* 処理が終了している状態 | |
* 食べ切れなかった残りの入力を持っていることに注意 | |
* err | |
* こんなの食べられないよの人 | |
* エラーが発生した状態 | |
!SLIDE | |
IterateeT の値達 プリミティブ編 | |
------------------------------- | |
```scala | |
def head[X, E, F[_] : Pointed]: IterateeT[X, E, F, Option[E]] = { | |
def step(s: Input[E]): IterateeT[X, E, F, Option[E]] = | |
s(empty = cont(step) | |
, el = e => done(Some(e), emptyInput[E]) | |
, eof = done(None, eofInput[E]) | |
) | |
cont(step) | |
} | |
``` | |
* 先頭一文字を返す Iteratee | |
* 関数 step を定義して cont(step) を返すのは頻出パターン | |
* 入力が空だった場合に、残りの処理として自分自身を返すため | |
!SLIDE | |
IterateeT の値達 合成編 | |
----------------------- | |
```scala | |
def head2[X, F[_] : Monad] : Iteratee[X, Char, F, Option[String]] = | |
for { | |
optc0 <- head[X, Char, F] | |
optc1 <- head[X, Char, F] | |
} yield for { | |
c0 <- opt0 | |
c1 <- opt1 | |
} yield "" + c0 + c1 | |
``` | |
* 先頭二文字を文字列として返す Iteratee | |
* コンパイルしてないので型エラーでたらごめんなさい… | |
* composable な入力処理が実現できている | |
* 型パラメタが鬱陶しいけど… | |
* F 高階型パラメタの Monad の制約が flatMap のために必要 | |
!SLIDE | |
いい加減コンテキストってなんなんですか | |
-------------------------------------- | |
* ぶっちゃけモナドです。以下のようなものがあります | |
* IO モナド -> I/O を伴うコンテキスト | |
* Option モナド -> 失敗を伴うコンテキスト | |
* List モナド -> 非決定性を伴うコンテキスト | |
* Id モナド -> 特に何もない、普通のコンテキスト | |
!SLIDE | |
つまりどういうこと? | |
-------------------- | |
* IterateeT[X, E, IO, A] は入出力を伴う StepT | |
* 入力を処理しつつ出力をしたいような場合はこっち | |
* ロギングやトレースのことを考えると常にこっちになってしまう気はする | |
* IterateeT[X, E, Id, A] は単なる StepT | |
* type Iteratee[X, E, A] = Iteratee[X, E, Id, A] という type alias が用意されています | |
!SLIDE | |
なんでこんなことするの? | |
------------------------ | |
* その入力処理が、I/O を伴うかどうかが型で区別できる | |
* I/O は副作用を引き起こすので、区別できると嬉しい、こともある | |
* ぶっちゃけ Haskell のマネ…! | |
* SIDE-EFFECT IS POWER!!!! | |
!SLIDE | |
scalaz.Iteratee.EnumeratorT | |
--------------------------- | |
```scala | |
trait EnumeratorT[X, E, F[_]] { | |
def apply[A]: StepT[X, E, F, A] => IterateeT[X, E, F, A] | |
} | |
``` | |
* StepT に入力を食べさせてあげる人 | |
* 食べさせたあとはコンテキストの中へ | |
!SLIDE | |
EnumeratorT のメソッド例 | |
------------------------ | |
* ここでコードを紹介できるくらい実装が簡単なものがなかった! | |
* map, flatMap メソッド | |
* おなじみのメソッド。でも実装は少しだけややこしい。 | |
* EnumeratorT は IterateeT => IterateeT のようなものだから簡単な気がする | |
* と思ったらそんなことはなかったぜ | |
* 複数の EnumeratorT を連結するメソッド | |
* があるはずなのに見つけられない!どこかにあるはず | |
!SLIDE | |
EnumeratorT の値達 プリミティブ編 その 1 | |
---------------------------------------- | |
```scala | |
def empty[X, E, F[_]: Pointed]: EnumeratorT[X, E, F] = | |
new EnumeratorT[X, E, F] { | |
def apply[A] = _.pointI | |
} | |
``` | |
* 空の入力を供給する Enumerator | |
* と思ったら実装これ emptyInput 供給してないじゃん!詐欺だよ! | |
!SLIDE | |
EnumeratorT の値達 プリミティブ編 その 2 | |
---------------------------------------- | |
```scala | |
def enumEofT[X, E, F[_] : Pointed]: EnumeratorT[X, E, F] = | |
new EnumeratorT[X, E, F] { | |
def apply[A] = _.mapCont(_(eofInput)) | |
} | |
``` | |
* EOF を供給する Enumerator | |
* これを供給してから IterateeT#run を呼ぶ | |
!SLIDE | |
EnumeratorT の値達 プリミティブ編 その 3 | |
---------------------------------------- | |
```scala | |
def enumOne[X, E, F[_]: Pointed](e: E): EnumeratorT[X, E, F] = | |
new EnumeratorT[X, E, F] { | |
def apply[A] = _.mapCont(_(elInput(e))) | |
} | |
``` | |
* 入力を一つだけ供給する Enumerator | |
!SLIDE | |
未完 | |
---- | |
* 残念 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment