Compositional I/ Streams in Scala Rnar Bjarnason, Verizon Labs @ - - PowerPoint PPT Presentation

compositional i streams in scala
SMART_READER_LITE
LIVE PREVIEW

Compositional I/ Streams in Scala Rnar Bjarnason, Verizon Labs @ - - PowerPoint PPT Presentation

Compositional I/ Streams in Scala Rnar Bjarnason, Verizon Labs @ runarorama QCon London, March 2016 Scalaz-Stream (FS2) F unctional S treams for S cala github.com/functional-streams-for-scala Disclaimer This library is changing.


slide-1
SLIDE 1

Compositional I/Ο Streams in Scala

Rúnar Bjarnason, Verizon Labs @runarorama QCon London, March 2016

slide-2
SLIDE 2

Scalaz-Stream (FS2)

Functional Streams for Scala

github.com/functional-streams-for-scala

slide-3
SLIDE 3
slide-4
SLIDE 4

Disclaimer

This library is changing.
 
 We’ll talk about the current version (0.8).

slide-5
SLIDE 5

Funnel — oncue.github.io/funnel http4s — http4s.org streamz — github.com/krasserm/streamz

slide-6
SLIDE 6

Scalaz-Stream (FS2)

a purely functional streaming I/O library for Scala

slide-7
SLIDE 7
  • Streams are essentially “lazy lists”
  • f data and effects.
  • Immutable and


referentially transparent

slide-8
SLIDE 8

Design goals

  • compositional
  • expressive
  • resource-safe
  • comprehensible
slide-9
SLIDE 9

import scalaz.stream._ io.linesR("testdata/fahrenheit.txt") .filter(s => !s.trim.isEmpty && !s.startsWith("//")) .map(line => fahrenheitToCelsius(line.toDouble).toString) .intersperse("\n") .pipe(text.utf8Encode) .to(io.fileChunkW("testdata/celsius.txt"))

slide-10
SLIDE 10

Process[Task,A]

slide-11
SLIDE 11

scalaz.concurrent.Task

  • Asynchronous
  • Compositional
  • Purely functional
slide-12
SLIDE 12

a Task is a first-class program

slide-13
SLIDE 13

a Task is a list of instructions

slide-14
SLIDE 14

Task is a monad

slide-15
SLIDE 15

a Task doesn’t do anything until you call .run

slide-16
SLIDE 16

Constructing Tasks

slide-17
SLIDE 17

Task.delay(readLine): Task[String] Task.now(42): Task[Int] Task.fail( new Exception("oops!") ): Task[Nothing]

slide-18
SLIDE 18

a: Task[A] pool: java.util.concurrent.ExecutorService Task.fork(a)(pool): Task[A]

slide-19
SLIDE 19

Combining Tasks

slide-20
SLIDE 20

a: Task[A] b: Task[B] val c: Task[(A,B)] = Nondeterminism[Task].both(a,b)

slide-21
SLIDE 21

a: Task[A] f: A => Task[B] val b: Task[B] = a flatMap f

slide-22
SLIDE 22

val program: Task[Unit] = for { _ <- delay(println("What's your name?")) n <- delay(scala.io.StdIn.readLine) _ <- delay(println(s"Hello $n")) } yield ()

slide-23
SLIDE 23

Running Tasks

slide-24
SLIDE 24

a: Task[A] a.run: A

slide-25
SLIDE 25

a: Task[A] k: (Throwable \/ A) => Unit a runAsync k: Unit

slide-26
SLIDE 26

scalaz.stream.Process

slide-27
SLIDE 27

Process[F[_],A]

slide-28
SLIDE 28

Process[Task,A]

slide-29
SLIDE 29

val halt: Process[Nothing,Nothing] def emit[A](a: A): Process[Nothing,A] def eval[F[_],A](eff: F[A]): Process[F,A]

Stream primitives

slide-30
SLIDE 30

Process.eval( Task.delay(readLine) ): Process[Task,String]

slide-31
SLIDE 31

def IO[A](a: => A): Process[Task,A] = Process.eval(Task.delay(a))

slide-32
SLIDE 32

Combining Streams

slide-33
SLIDE 33

p1: Process[F,A] p2: Process[F,A] val p3: Process[F,A] = p1 append p2

slide-34
SLIDE 34

p1: Process[F,A] p2: Process[F,A] val p3: Process[F,A] = p1 ++ p2

slide-35
SLIDE 35

val twoLines: Process[Task,String] = IO(readLine) ++ IO(readLine)

slide-36
SLIDE 36

val stdIn: Process[Task,String] = IO(readLine) ++ stdIn

slide-37
SLIDE 37

val stdIn: Process[Task,String] = IO(readLine).repeat

slide-38
SLIDE 38

val cat: Process[Task,Unit] = stdIn flatMap { s => IO(println(s)) }

slide-39
SLIDE 39

val cat: Process[Task,Unit] = for { s <- stdIn _ <- IO(println(s)) } yield ()

slide-40
SLIDE 40

def grep(r: Regex): Process[Task,Unit] = { val p = r.pattern.asPredicate.test _ def out(s: String) = IO(println(s)) stdIn filter p flatMap out }

slide-41
SLIDE 41

Running Processes

slide-42
SLIDE 42

p: Process[Task,A] p.run: Task[Unit]

slide-43
SLIDE 43

p: Process[Task,A] p.runLog: Task[List[A]]

slide-44
SLIDE 44

p: Process[F,A] B: Monoid f: A => B p runFoldMap f: F[B]

slide-45
SLIDE 45

F: Monad p: Process[F,A] p.run: F[Unit]

slide-46
SLIDE 46

Sinks

slide-47
SLIDE 47

x : Process[F,A] y : Sink[F,A] x to y : Process[F,Unit]

slide-48
SLIDE 48

import scalaz.stream.io io.stdInLines: Process[Task,String] io.stdOutLines: Sink[Task,String] val cat = io.stdInLines to io.stdOutLines

slide-49
SLIDE 49

A sink is just a stream of functions

slide-50
SLIDE 50

type Sink[F[_],A] = Process[F, A => Task[Unit]]

slide-51
SLIDE 51

val stdOut: Sink[Task,String] = IO { s => Task.delay(println(s)) }.repeat

slide-52
SLIDE 52

Pipes

slide-53
SLIDE 53

as: Process[F,A] p: Process1[A,B] as pipe p: Process[F,B]

slide-54
SLIDE 54

as: Process[F,A] val p = process1.chunk(10) as pipe p: Process[F,Vector[A]]

slide-55
SLIDE 55

Process.await1[A]: Process1[A,A]

slide-56
SLIDE 56

def take[I](n: Int): Process1[I,I] = if (n <= 0) halt else await1[I] ++ take(n - 1)

slide-57
SLIDE 57

def distinct[A]: Process1[A,A] = { def go(seen: Set[A]): Process1[A,A] = Process.await1[A].flatMap { a => if (seen(a)) go(seen) else Process.emit(a) ++ go(seen + a) } go(Set.empty) }

slide-58
SLIDE 58

Multiple sources

slide-59
SLIDE 59

as: Process[F,A] bs: Process[F,B] t: Tee[A,B,C] (as tee bs)(t): Process[F,C]

slide-60
SLIDE 60

tee.zip: Tee[A,B,(A,B)] tee.interleave: Tee[A,A,A]

slide-61
SLIDE 61

val add: Tee[Int,Int,Int] = { for { x <- awaitL[Int] y <- awaitR[Int] } yield x + y }.repeat val sumEach = (p1 tee p2)(add)

slide-62
SLIDE 62

as: Process[Task,A] bs: Process[Task,B] y: Wye[A,B,C] (as wye bs)(y): Process[Task,C]

slide-63
SLIDE 63

ps: Process[F,Process[F,A]] merge.mergeN(ps): Process[F,A]

slide-64
SLIDE 64

scalaz.stream.async

slide-65
SLIDE 65

Queues & Signals

slide-66
SLIDE 66

trait Queue[A] { ... def enqueue: Sink[Task,A] def dequeue: Process[Task,A] ... }

slide-67
SLIDE 67

import scalaz.stream.async._ def boundedQueue[A](n: Int): Queue[A] def unboundedQueue[A]: Queue[A] def circularBuffer[A](n: Int): Queue[A]

slide-68
SLIDE 68

trait Signal[A] { ... def get: Task[A] def set(a: A): Task[Unit] ... }

slide-69
SLIDE 69

trait Signal[A] { ... def discrete: Process[Task,A] def continuous: Process[Task,A] ... }

slide-70
SLIDE 70

Demo: Internet Relay Chat

slide-71
SLIDE 71

github.com/runarorama/ircz Server: 38 lines of Scala Client: 14 lines of Scala Uses scalaz-netty

slide-72
SLIDE 72

github.com/runarorama/ircz

def serve(address: InetSocketAddress) = merge.mergeN { Netty serve address map { client => for { c <- client _ <- IO(clients += c.sink) _ <- c.source to messageQueue.enqueue } yield () } }

slide-73
SLIDE 73

github.com/runarorama/ircz

val relay = for { message <- messageQueue.dequeue client <- emitAll(clients) _ <- emit(message) to client } yield ()

slide-74
SLIDE 74

github.com/runarorama/ircz

val main = (serve wye relay)(wye.merge)

slide-75
SLIDE 75

github.com/runarorama/ircz

client = for { c <- Netty connect Server.address in = c.source .pipe(text.utf8Decode) .to(io.stdOutLines)

  • ut = io.stdInLines

.pipe(text.utf8Encode) .to(c.sink) _ <- (in wye out)(wye.merge) } yield ()

slide-76
SLIDE 76

github.com/functional-streams-for-scala github.com/runarorama/ircz

  • ncue.github.io/funnel