- versionは0.7a
- scalaz-streamではなくscalaz-concurrentにあるクラス
- scalaz-streamは、これと
Task
にかなり依存してるので、まずはこれらを理解することが重要 - Scala標準Futureと違い
- 走っている状態 は存在しない。純粋なデータ構造である
- エラー処理は行わない。別スレッドで走らせた際に例外発生したら、永遠にブロックする。つまり、実質
Task
として使うことがほとんどで、Future
を直接使うことはない - 例の本 Scala関数型デザイン&プログラミング ―Scalazコントリビューターによる関数型徹底ガイド の7章で説明している
Par
と基本的には同じものなので、それを読むとよい - 内部の低レベルな部分は
java.util.concurrent
のConcurrentLinkedQueue
,CountDownLatch
,AtomicInteger
,AtomicBoolean
,AtomicReference
やscala.concurrent.SyncVar
などを使って実装されている
class Task[+A](val get: Future[Throwable \/ A])
というもの。FutureはScala標準のものではなく、ScalazのFuture- これも同じくscalaz-streamのクラスではなくscalaz-concurrentのクラス
- 依存してる
scodec-bits
のクラス - immutableなByte列
- scalaz-streamでは
Array[Byte]
の代わりに大抵コレが使われる - Haskellのattoparsecのアレを真似したの?
- https://github.com/scodec/scodec-bits/blob/v1.0.6/core/src/main/scala/scodec/bits/ByteVector.scala#L1631
- http://www.serpentine.com/blog/2014/05/31/attoparsec/ C言語より速い!?とかいうやつ
- scalaz-stream内で、一番最重要で基礎となるクラス
- コレクションのように扱え、0個以上の
O
(2つめの型パラメータ)のシーケンスと捉えることも可能だが、実態は状態マシンである - 事実、コレクションにあるようなメソッドの大半が
Process
にもある F[_]
やO
に当てはめる型パラメータの種類によって、入力元も出力先も表す- つまり、IterateeにおけるEnumeratorやEnumerateeなどに相当するものも、全部
Process
で表現する - 例の本 Scala関数型デザイン&プログラミング ―Scalazコントリビューターによる関数型徹底ガイド の15章で解説されているものは、ある意味これ
- ただし、本のほうは、Stackoverflow回避のためのトランポリンがされていなかったり、もう少し単純
Process1
,Process0
,Tee
,Wye
などとして使う場合を除けばF[_]
には大抵の場合Task
が入るはず。もしくはIO
- なぜかというと、
F[_]
がscalaz.Catchable
を要求するメソッドがあり、scalaz.Catchable
のインスタンスはかなり限られているため - 無条件で
MonadPlus
MonadPlus[({ type λ[α] = Process[F, α] })#λ]
- 純粋なメモリ上のシーケンスの表現?
Process0
に限らないが、Nothing
をあてはめたり、varianceを使ったり、良くも悪くもすべてをProcess
とそのtype aliasで表現することにより、わかりずらさの一因になってる気はする
I
が入力の型、O
が出力の型になるらしい- 普通に考えると
Process
でProcess1
を表現するのは不可能に近いが、謎技術により実現されている - https://github.com/scalaz/scalaz-stream/blob/release/0.7a/src/main/scala/scalaz/stream/Process.scala#L915-L944
- 例の本にもでてきている
scalaz.Choice
やscalaz.Profunctor
のインスタンスであるChannel
とは違い、副作用がない純粋な関数を表す?(このあたりの理解あまり自信がない)- これ以上うまく説明できるほどには理解できてないので、あとで
- 消費する側のやつ
Process
を直接使う場合同様F[_]
は大抵の場合Task
かIO
def lift[F[_], A](f: A => F[Unit]): Sink[F, A]
というメソッドで生成できる- https://github.com/scalaz/scalaz-stream/blob/release/0.7a/src/main/scala/scalaz/stream/sink.scala#L6
- 標準で用意されている、
Sink
を生成するメソッド例 def chunkW(os: => OutputStream): Sink[Task,ByteVector]
任意のOutputStream
をByteVector
のSink
に変換def stdOut: Sink[Task,String]
標準出力を表現するSink
F
はTask
やIO
,I
は入力,O
は出力Process1
とは違い、大抵副作用があるものの表現につかう?- この言い方で適切なのか、ちょっと自信ない
- というか、
F[_]
にTask
が入ることにより、Channel
やChannel
を走らせたTask
自体は純粋なデータ構造で、最後にTask
を走らせる際に副作用があるので、そこを勘違いしないように - 標準で用意されている、
Channel
を返すメソッド例 def chunkR(is: => InputStream): Channel[Task,Int,ByteVector]
元のInputStream
からのbyte数を引数にとり、受け取ったByte数毎にわけて出力が取得できる、というもの
- 2つの入力を合成して、1つにすることを表現する関数というか状態マシン
- 標準で用意されている
Tee
を返すメソッド例 def zipWith[I, I2, O](f: (I, I2) => O): Tee[I, I2, O]
純粋な関数からTee生成
- TODO あとで書く
- TODO あとで書く
- TODO あとで書く
- TODO あとで書く
- TODO あとで書く
- TODO あとで書く