More Related Content Similar to Scalaz-StreamによるFunctional Reactive Programming (20) More from Tomoharu ASAMI (20) Scalaz-StreamによるFunctional Reactive Programming2. 自己紹介
• 1985年年富⼠士通(株)⼊入社
• UNIXワークステーション/サーバーのOS、分散基盤、Web基盤の開発
に従事
• 2001年年9⽉月に独⽴立立
• Java, XML, UMLを中⼼心に活動
• 2005年年4⽉月より2007年年3⽉月まで
• 稚内北北星学園⼤大学東京サテライト校教授
• 現在
• (株) 匠BusinessPlace 取締役チーフコンサルタント
• (株) Everforth 取締役CTO
• OSS
• SmartDoc
• Relaxer
• 著作
• 上流流⼯工程UMLモデリング(⽇日経BP)
• マインドマップではじめるモデリング講座(翔泳社)
• Relaxer Java/XMLによるWeb開発(ピアソン)
• ぼくらのScala(Softbank Creative)
7. Scalaz
• https://github.com/scalaz/scalaz
• キャッチフレーズ
• 昔: Scalaz: Type Classes and Pure Functional Data
Structures for Scala
• 今: An extension to the core Scala library for functional
programming. http://typelevel.org
• 最新の関数型プログラミングを可能にする機能群を
Scala向けに⽤用意
• 型クラス
• 純粋関数型データ構造
• Haskellで実績のある機能群をScalaで実現
8. Pipelineプログラミング(1)
def pipeline(x: Int) = h(g(f(x))
def pipeline(x: Option[Int]) = x.map(f).map(g).map(h)
def pipeline(x: Int) = x |> f |> g |> h
関数呼び出し
Functor
Monad
def pipeline(x: Option[Int]) = x.flatMap(f).flatMap(g).flatMap(h)
def pipeline(x: Option[Int]) =
for (a <- f(x); b <- g(a); c <- h(b)) yiled c
9. Pipelineプログラミング(2)
val pipeline =
State(f).flatMap(x => State(g(x))).flatMap(x => State(h(x)))
pipeline.run(10)
val pipeline =
for (a <- State(f);b <- State(g(a)); c <- State(h(c))) yield c
pipeline.run(10)
Monad (インタープリター型)
参考: [FP] パイプライン・プログラミング
http://modegramming.blogspot.jp/2015/06/fp.html
val pipeline = io.linesR("infile.txt").filter(_.nonEmpty).map(x =>
s"${x.length}:$x”)
pipeline.run.run
scalaz-stream (Process Monad)
13. The Reactive Manifest
• http://www.reactivemanifesto.org/
• ⽇日本語訳
• http://okapies.hateblo.jp/entry/2014/12/03/025921
• Responsive
• 応答性:すぐ応答する
• Resilient
• 耐故障性:回復復⼒力力に富む、⽴立立ち直りが早い
• Elastic
• 弾⼒力力性:伸縮⾃自在の
• Message Driven
• メッセージ駆動
• 関連: Reactive Streams
• http://www.reactive-streams.org/
16. Functional Reactive Programming
の候補
• RxJava – Scala
• https://github.com/ReactiveX/RxJava
• Observableモナド
• Scalaz Stream
• https://github.com/scalaz/scalaz-stream
• Processモナド
• Akka Streams
• https://typesafe.com/blog/typesafe-announces-akka-
streams
• Akka actor
19. 部品 (ストリーム)
• Process
• Processモナド。
• Process1
• データ変換。Processの⼀一種。
• Sink
• ストリームの終端。副作⽤用あり。Processの⼀一種。
• Channel
• 外部⼊入出⼒力力。Processの⼀一種。
• Tee
• 2つのストリームを合流流(interleave, zipping)。Processの⼀一種。
• Wye
• 2つのストリームを⾮非決定的に合流流。Processの⼀一種。
• Writer
• ストリームを2本に分割。Processの⼀一種。
20. 部品 (連携)
• Exchange
• 外部システムと連携。
• Queue
• キュー。
• 書込み⽤用のメソッドを提供。
• 読込み⽤用のProcessモナドを⽣生成。
• Topic
• pub/sub機能。
• 書込み⽤用のメソッドを提供。
• 書込み⽤用のProcessモナドを⽣生成。
• 読込み⽤用のProcessモナドを⽣生成。
21. scalaz-streamの⻑⾧長所/短所
• ⻑⾧長所
• Scalazと親和性が⾼高い。
• 純粋関数型の範囲で相当のことができる。
• 部品化が容易易。
• プログラミングが容易易。
• 短所
• 性能が遅いようです。
• COMPARING AKKA-STREAM AND SCALAZ-STREAM WITH
CODE EXAMPLES
• https://softwaremill.com/comparing-akka-stream-scalaz-stream
• (最初に)使い⽅方を把握するのが難しい。
• ストリームの分岐は関数型の範囲ではできない。
• Topicを使⽤用して実現。
• EIP:Content-Based Routingなど。
• マシンをまたいだストリームは構築できない。
22. scalaz-streamプログラム
val source: Process[Task, String] = io.linesR("infile.txt")
val a: Process[Task, String] = source.filter(_.nonEmpty)
val b: Process[Task, String] = a.map(x => s"${x.length}:$x")
val c: Process[Task, ByteVector] = b.pipe(text.utf8Encode)
val sink: Sink[Task, ByteVector] = io.fileChunkW("outfile.txt")
val pipeline: Process[Task, Unit] = b.to(sink)
val task: Task[Unit] = pipeline.run
val result: Unit = task.run
io.linesR("infile.txt").filter(_.nonEmpty).map(x => s"${x.length}:$x").
pipe(text.utf8Encode).to(io.fileChunkW("outfile.txt")).run.run
24. 準備
case class Packet(seqno: Int, end: Boolean, content: String)
def sink: Sink[Task, Packet] = {
io.channel((a: Packet) => Task.delay(println(a)))
}
参考: [scalaz-stream] ストリーミングで状態機械
http://modegramming.blogspot.jp/2015/04/scalaz-stream.html
参考: [scalaz-stream] Scala的状態機械/OOP編
http://modegramming.blogspot.jp/2015/03/scalaoop.html
参考: [scalaz-stream] Scala的状態機械/FP編
http://modegramming.blogspot.jp/2015/03/scalafp.html
25. アプリケーション・ロジック
def buildTextToPacket[M[_]: Monad](source: Process[M, String]):
Process[M, Packet] = {
source.
chunk(3). // 3個ずつチャンク化
pipe(zipWithNext). // 次のパケットを同時に取得
pipe(zipWithIndex). // インデックを採番
map(toPacket)
}
def toPacket(x: ((Vector[String], Option[Vector[String]]), Int)):
Packet = {
val ((current, next), index) = x
val content = current.mkString("-")
val isend = next.cata(_.headOption === Some("start"), true)
Packet(index + 1, isend, content)
}
既存部品とアプリケーションロ
ジックを組合せてパイプライン
を構築
以下のロジックを実現
- 3つで⼀一つのパケットにする
- パケットにシーケンス番号をつける
- “start”データの前のパケットにエン
ドマークをつける
26. ⼤大規模データ処理理
val source = io.linesR("data.txt")
val pipeline = buildTextToPacket(source).to(sink)
pipeline.run.run
Packet(1,false,1-2-3)
Packet(2,true,4-5-6)
Packet(3,true,start-8-9)
1
2
3
4
5
6
start
8
9
汎⽤用部品を⼤大規模データ処理理に
適⽤用
リソース管理
フロー制御
chunk
zipWith
Next
zipWith
Index
toPacketChannel Sink
Process Monad
27. EventProcessor
val queue = EventProcessor.q
val source = Vector("1", "2", "3", "4", "5", "6", "start", "8", "9")
source foreach { x =>
queue.enqueueOne(x).run
Thread.sleep(2000)
}
object EventProcessor {
val q = async.unboundedQueue[String]
val eventStream: Process[Task, String] = q.dequeue
}
参考: [scalaz-stream] ストリーミングで状態機械
http://modegramming.blogspot.jp/2015/04/scalaz-stream.html
28. ストリーム処理理
val source = EventProcessor.eventStream
val pipeline = buildTextToPacket(source).to(sink)
pipeline.run.run
Packet(1,false,1-2-3)
Packet(2,true,4-5-6)
汎⽤用ロジックをストリーミング
処理理に適⽤用
リソース管理
フロー制御
chunk
zipWith
Next
zipWith
Index
toPacketChannel Sink
Process Monad
29. まとめ
• Reactiveには関数型プログラミング
• Responsive, Elastice, Resilience, Message-Driven
• Cuncurrent, Parallel, Distributed
• 純粋関数型と普通の関数型/⼿手続き型のトレードオフ
• 書やすさ/品質/部品化/将来技術との連続性 vs 性能
• 可能であれば純粋関数型が望ましい
• scalaz stream
• 純粋関数型の範囲で相当のことができる
• 出きない/苦⼿手なことがあるので応⽤用との相性を⾒見見極める
• Push
• マシンをまたいだストリームの構築