John A. De Goes
@jdegoes - degoes.net
SCALAZ STREAM
REBIRTH
Scale By The Bay · November 16 · San Francisco
Itamar Ravid
@itrvd - iravid.com
#ZIO
#SCALEBYTHEBAY
PERFORMANCE
OVERVIEW
MOTIVATION API DESIGN
1 Why Streams & Why Scalaz Stream
MOTIVATION
Why Streams?
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Infinite Input
Sets
Leak-Free Data
Processing
Incremental
Computation
∞
File & Socket
Processing
Graph
Processing
Reactive &
Dataflow
Akka Streams FS2
Scalaz ReactiveGraphJet
Why ZIO Stream?
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Why ZIO Stream?
File & Socket
Processing
Graph
Processing
Reactive &
Dataflow
Akka Streams FS2
Scalaz ReactiveGraphJet
ZIO STREAM
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Why ZIO Stream?
PERFORMANCEMOTIVATION API DESIGN SUMMARY
𐄂 Cycles𐄂 Cons 𐄂 Uncons
ZIO Stream Old Scalaz Stream Akka Streams FS2
Lazy ✓ 𐄂 𐄂 ✓
Leak-Free ✓ 𐄂 𐄂 On a good day
Uniform ✓ NA NA 𐄂
Type Inferred ✓ 𐄂 ✓ 𐄂
Economical ✓ 𐄂 𐄂 ✓
ZIO Integration ✓ 𐄂 𐄂 𐄂
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Why ZIO Stream?
2 What Scalaz Stream Looks Like
API
PERFORMANCEMOTIVATION API DESIGN SUMMARY
AkkaStream AbruptTerminationException AbstractShape ActorAttributes ActorMaterializer ActorMaterializerSettings
AmorphousShape Attributes BidiShape BindFailedException BufferOverflowException Client ClosedShape
ConnectionException DelayOverflowStrategy EagerClose FanInShape FanInShape10 FanInShape11 FanInShape12
FanInShape13 FanInShape14 FanInShape15 FanInShape16 FanInShape17 FanInShape18 FanInShape19 FanInShape1N
FanInShape2 FanInShape20 FanInShape21 FanInShape22 FanInShape3 FanInShape4 FanInShape5 FanInShape6
FanInShape7 FanInShape8 FanInShape9 FanOutShape FanOutShape10 FanOutShape11 FanOutShape12 FanOutShape13
FanOutShape14 FanOutShape15 FanOutShape16 FanOutShape17 FanOutShape18 FanOutShape19 FanOutShape2
FanOutShape20 FanOutShape21 FanOutShape22 FanOutShape3 FanOutShape4 FanOutShape5 FanOutShape6
FanOutShape7 FanOutShape8 FanOutShape9 FlowMonitor FlowMonitorState FlowShape Fusing Graph IgnoreBoth
IgnoreCancel IgnoreComplete Inlet InPort IOResult KillSwitch KillSwitches MaterializationContext MaterializationException
Materializer MaterializerLoggingProvider Outlet OutPort OverflowStrategy QueueOfferResult RateExceededException
Server Shape SharedKillSwitch SinkShape SourceShape StreamLimitReachedException
StreamSubscriptionTimeoutSettings StreamSubscriptionTimeoutTerminationMode StreamTcpException
SubstreamCancelStrategy Supervision ThrottleMode TLSClientAuth TLSClosing TLSProtocol TLSRole UniformFanInShape
UniformFanOutShape UniqueKillSwitch
PERFORMANCEMOTIVATION API DESIGN SUMMARY
AkkaStream AbruptTerminationException AbstractShape ActorAttributes ActorMaterializer ActorMaterializerSettings
AmorphousShape Attributes BidiShape BindFailedException BufferOverflowException Client ClosedShape
ConnectionException DelayOverflowStrategy EagerClose FanInShape FanInShape10 FanInShape11 FanInShape12
FanInShape13 FanInShape14 FanInShape15 FanInShape16 FanInShape17 FanInShape18 FanInShape19 FanInShape1N
FanInShape2 FanInShape20 FanInShape21 FanInShape22 FanInShape3 FanInShape4 FanInShape5 FanInShape6
FanInShape7 FanInShape8 FanInShape9 FanOutShape FanOutShape10 FanOutShape11 FanOutShape12 FanOutShape13
FanOutShape14 FanOutShape15 FanOutShape16 FanOutShape17 FanOutShape18 FanOutShape19 FanOutShape2
FanOutShape20 FanOutShape21 FanOutShape22 FanOutShape3 FanOutShape4 FanOutShape5 FanOutShape6
FanOutShape7 FanOutShape8 FanOutShape9 FlowMonitor FlowMonitorState FlowShape Fusing Graph IgnoreBoth
IgnoreCancel IgnoreComplete Inlet InPort IOResult KillSwitch KillSwitches MaterializationContext MaterializationException
Materializer MaterializerLoggingProvider Outlet OutPort OverflowStrategy QueueOfferResult RateExceededException
Server Shape SharedKillSwitch SinkShape SourceShape StreamLimitReachedException
StreamSubscriptionTimeoutSettings StreamSubscriptionTimeoutTerminationMode StreamTcpException
SubstreamCancelStrategy Supervision ThrottleMode TLSClientAuth TLSClosing TLSProtocol TLSRole UniformFanInShape
UniformFanOutShape UniqueKillSwitch
Stream Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
ZIO STREAM
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Stream[E, A]
May fail with a checked error of type E
May effectfully produce zero or more values of type A
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Monoid
append
zero
Applicative
map
point
ap
Monad
bind
Bifunctor
leftMap
bimap
Alternative
alt
empty
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Stream.empty
Stream.point(v)
Stream.fromIterable(list)
Stream.liftIO(io)
Stream.fromQueue(queue)
Stream.unfold(s)(f)
Stream.unfoldM(s)(f)
Stream.bracket(open)(close)(read)
val empty: Stream[Nothing, Nothing] =
Stream.empty
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val singleton: Stream[Nothing, Int] =
Stream.point(42)
Stream.empty
Stream.point(v)
Stream.fromIterable(list)
Stream.liftIO(io)
Stream.fromQueue(queue)
Stream.unfold(s)(f)
Stream.unfoldM(s)(f)
Stream.bracket(open)(close)(read)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val hundred: Stream[Nothing, Int] =
Stream.fromIterable(0 to 100)
Stream.empty
Stream.point(v)
Stream.fromIterable(list)
Stream.liftIO(io)
Stream.fromQueue(queue)
Stream.unfold(s)(f)
Stream.unfoldM(s)(f)
Stream.bracket(open)(close)(read)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val userIn: Stream[IOException, String] =
Stream.liftIO(getStrLn)
Stream.empty
Stream.point(v)
Stream.fromIterable(list)
Stream.liftIO(io)
Stream.fromQueue(queue)
Stream.unfold(s)(f)
Stream.unfoldM(s)(f)
Stream.bracket(open)(close)(read)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val intQueue: Queue[Int] = ...
val intStream: Stream[Nothing, Int] =
Stream.fromQueue(intQueue)
Stream.empty
Stream.point(v)
Stream.fromIterable(list)
Stream.liftIO(io)
Stream.fromQueue(queue)
Stream.unfold(s)(f)
Stream.unfoldM(s)(f)
Stream.bracket(open)(close)(read)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val integers: Stream[Nothing, Int] =
Stream.unfold(0)(i => (i, i + 1))
Stream.empty
Stream.point(v)
Stream.fromIterable(list)
Stream.liftIO(io)
Stream.fromQueue(queue)
Stream.unfold(s)(f)
Stream.unfoldM(s)(f)
Stream.bracket(open)(close)(read)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val latency: Stream[Nothing, Long] =
Stream.unfoldM(0 -> 0) {
case (n, total) =>
api.measureLatency(l =>
(n + 1) -> (total + n))
}
Stream.empty
Stream.point(v)
Stream.fromIterable(list)
Stream.liftIO(io)
Stream.fromQueue(queue)
Stream.unfold(s)(f)
Stream.unfoldM(s)(f)
Stream.bracket(open)(close)(read)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val fileBytes: Stream[IOException, Byte] =
Stream.bracket(openFile)(closeFile(_)) {
file => file.readByte.attempt.toOption
}
Stream.empty
Stream.point(v)
Stream.fromIterable(list)
Stream.liftIO(io)
Stream.fromQueue(queue)
Stream.unfold(s)(f)
Stream.unfoldM(s)(f)
Stream.bracket(open)(close)(read)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val integers: Stream[Nothing, Int] =
Stream.unfold(0)(i => (i, i + 1))
val integerDigits: Stream[Nothing, Int] =
integers.map(_.toString).map(_.length)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val users: Stream[Exception, User] =
...
val usersWithProfile:
Stream[Exception, (User, Profile)] =
for {
user <- users
prof <- Stream.liftIO(getProfile(user.id))
} yield (user, prof)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val usersWithProfile:
Stream[Exception, (User, Profile)] = …
val targetUsers: Stream[Exception, User] =
usersWithProfile.filter(user =>
user.age >= 18 &&
user.age <= 32 &&
user.city == "San Francisco" &&
user.interests.contains("technology"))
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
type File = Stream[IOException, Report]
val monthlyFiles: List[File] = …
val allFiles =
monthlyFiles.foldLeft[File]
(Stream.empty)(_ ++ _)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val stream:
Stream[IOException, Chunk[Byte]] =
Stream.bracket(openFile)(
closeFile)(readChunk)
val usersJ: Stream[IOException, Json] =
stream.transduce(toJson)
.filter(isUserRecord)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val integers: Stream[Nothing, Int] =
Stream.unfold(0)(i => (i, i + 1))
val averages: Stream[Nothing, Int] =
integers.scan(0 -> 0) {
case ((sum, count), int) =>
(sum + int, count + 1) ->
if (count == 0) Int.MaxValue
else sum / count
}
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val integers: Stream[Nothing, Int] =
Stream.unfold(0)(i => (i, i + 1))
val first100Evens: Stream[Nothing, Int] =
integers.filter(_ % 2 == 0).take(100)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val integers: Stream[Nothing, Int] =
Stream.unfold(0)(i => (i, i + 1))
val evensUnder100: Stream[Nothing, Int] =
integers.filter(_ % 2 == 0)
.takeWhile(_ < 100)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val integers: Stream[Nothing, Int] =
Stream.unfold(0)(i => (i, i + 1))
val evensAfter100: Stream[Nothing, Int] =
integers.filter(_ % 2 == 0).drop(100)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val integers: Stream[Nothing, Int] =
Stream.unfold(0)(i => (i, i + 1))
val evensOver100: Stream[Nothing, Int] =
integers.filter(_ % 2 == 0)
.dropWhile(_ < 100)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val inputs: Stream[IOException, String] =
Stream.liftIO(getStrLn).forever
val echo: IO[IOException, Unit] =
inputs.foreach(putStrLn)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val integers: Stream[Nothing, Int] =
Stream.unfold(0)(i => (i, i + 1))
val users: Stream[Exception, User] = …
val usersWithId:
Stream[Exception, (Int, User)] =
integers.zip(users)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val stream1: Stream[IOException, JSON] =
kafka.stream("events1")
val stream2: Stream[IOException, JSON] =
kafka.stream("events2")
val both: Stream[IOException, JSON] =
stream1.merge(stream2)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val file1:
Stream[IOException, User] =
readFile("file1.data")
.transduce(toUser)
val file2:
Stream[IOException, User] =
readFile("file2.data")
.transduce(toUser)
val fastest: Stream[IOException, User] =
file1.joinWith(file2)(_ race _)
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Stream
PERFORMANCEMOTIVATION API DESIGN SUMMARY
type StreamIO[A] = Stream[IOException, A]
val bytes: StreamIO[Byte] = …
val headersAndContent:
Managed[IOException,
(ContentType, StreamIO[Byte])] =
bytes.peel(parseContentType)
headersAndContent.use {
case (ContentType(Json), content) =>
content.transduce(toJson)...
case (ContentType(Xml), content) =>
content.transduce(toXml)...
}
stream.map(f)
stream.flatMap(f)
stream.filter(f)
stream1 ++ stream2
stream.transduce(what)
stream.scan(z)(f)
stream.take(n)
stream.takeWhile(f)
stream.drop(n)
stream.dropWhile(f)
stream.foreach(f)
stream1.zip(stream2)
stream1.merge(stream2)
stream1.joinWith(stream2)(f)
stream.peel(what)
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Sink[E, A0, A, B]
May fail with a checked error of type E
May effectfully produce a result of type B
Consumes values of type A
May possibly output a remainder of type A0
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
trait Stream[E, A] {
def run[R](sink: Sink[E, A, A, R]): IO[E, R]
}
myStream.run(Sink.drain) // IO[E, Unit]
myStream.run(decodeRequest) // IO[E, Request]
myStream.run(Sink.collect) // IO[E, List[A]]
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Monoid
append
zero
Applicative
map
point
ap
Monad
bind
Bifunctor
leftMap
bimap
Alternative
alt
empty
Category
id
compose
Profunctor
lmap
rmap
dimap
Strong
first
second
Choice
left
right
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val answer: Sink[Nothing, Nothing,
Any, Int] =
Sink.point(42)
Sink.point(value)
Sink.liftIO(ioValue)
Sink.lift(f)
Sink.fail(e)
Sink.await
Sink.drain
Sink.collect
Sink.fold(init)(step)
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val lifted: Sink[Throwable, Nothing,
Any, Array[Byte]] =
Sink.liftIO(IO.syncThrowable(dangerousOp))
Sink.point(value)
Sink.liftIO(ioValue)
Sink.lift(f)
Sink.fail(e)
Sink.await
Sink.drain
Sink.collect
Sink.fold(init)(step)
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val squared: Sink[Unit, Nothing,
Int, Int] =
Sink.lift(i => i * i)
Sink.point(value)
Sink.liftIO(ioValue)
Sink.lift(f)
Sink.fail(e)
Sink.await
Sink.drain
Sink.collect
Sink.fold(init)(step)
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val failure: Sink[DomainError, Nothing,
Any, Nothing] =
Sink.fail(BadOperation)
Sink.point(value)
Sink.liftIO(ioValue)
Sink.lift(f)
Sink.fail(e)
Sink.await
Sink.drain
Sink.collect
Sink.fold(init)(step)
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val json: Sink[Unit, Nothing,
Json, Json] =
Sink.await
Sink.point(value)
Sink.liftIO(ioValue)
Sink.lift(f)
Sink.fail(e)
Sink.await
Sink.drain
Sink.collect
Sink.fold(init)(step)
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val drain: Sink[Nothing, Nothing,
Any, Unit] =
Sink.drain
Sink.point(value)
Sink.liftIO(ioValue)
Sink.lift(f)
Sink.fail(e)
Sink.await
Sink.drain
Sink.collect
Sink.fold(init)(step)
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val collected: Sink[Nothing, Nothing,
Json, List[Json]] =
Sink.collect[Json]
Sink.point(value)
Sink.liftIO(ioValue)
Sink.lift(f)
Sink.fail(e)
Sink.await
Sink.drain
Sink.collect
Sink.fold(init)(step)
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val parser: Sink[Nothing, Char,
Char, Either[Error, Int]] =
Sink.fold((ParserState.initial, List[Int]()) {
case (ParserState.Digits, c) if c.isDigit =>
// ...
}
Sink.point(value)
Sink.liftIO(ioValue)
Sink.lift(f)
Sink.fail(e)
Sink.await
Sink.drain
Sink.collect
Sink.fold(init)(step)
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val digits: Sink[Nothing, Char,
Char, List[Char]] =
Sink.readWhile[Char](_.isDigit)
val number: Sink[Nothing, Char,
Char, Int] =
Sink.map(_.mkString.toInt)
sink.map(f)
sink.contramap(f)
sink.mapError(f)
sink.flatMap(f)
sink ~ otherSink
sink.?
sink.raceBoth(other)
sink.orElse(other)
sink.repeat
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val summing: Sink[Nothing, Int, Int, Int] =
Sink.fold(0)(_ + _)
case class Person(name: String, salary: Int)
val salarySum:
Sink[Nothing, Int, Person, Int] =
Sink.contramap[Person](_.salary)
sink.map(f)
sink.contramap(f)
sink.mapError(f)
sink.flatMap(f)
sink ~ otherSink
sink.?
sink.raceBoth(other)
sink.orElse(other)
sink.repeat
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val err: Sink[DomainError, Nothing,
Any, Nothing] =
Sink.fail(DomainError)
val errs: Sink[AppError, Nothing,
Any, Nothing] =
Sink.mapError(AppError(_))
sink.map(f)
sink.contramap(f)
sink.mapError(f)
sink.flatMap(f)
sink ~ otherSink
sink.?
sink.raceBoth(other)
sink.orElse(other)
sink.repeat
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val sumOfConsecutive:
Sink[Nothing, Nothing,
Int, Int] =
for {
a <- Sink.await
b <- Sink.await
} yield a + b
sink.map(f)
sink.contramap(f)
sink.mapError(f)
sink.flatMap(f)
sink ~ otherSink
sink.?
sink.raceBoth(other)
sink.orElse(other)
sink.repeat
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val parseHeader: Sink[Err, Byte,
Byte, Header] =
…
val parseBody: Sink[Err, Byte,
Byte, Body] =
…
val parseHeaderAndBody:
Sink[Err, Byte, Byte, (Header, Body)] =
parseHeader ~ parseBody
sink.map(f)
sink.contramap(f)
sink.mapError(f)
sink.flatMap(f)
sink ~ otherSink
sink.?
sink.raceBoth(other)
sink.orElse(other)
sink.repeat
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val parseHeader: Sink[Err, Byte,
Byte, Header] =
…
val maybeParseHeader:
Sink[Nothing, Byte,
Byte, Option[Header]] =
parseHeader ?
sink.map(f)
sink.contramap(f)
sink.mapError(f)
sink.flatMap(f)
sink ~ otherSink
sink.?
sink.raceBoth(other)
sink.orElse(other)
sink.repeat
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val request1: Sink[Err, Byte,
Byte, Req1] = …
val request2: Sink[Err, Byte,
Byte, Req2] = …
val request:
Sink[Err, Byte,
Byte, Either[Req1, Req2]] =
request1 raceBoth request2
sink.map(f)
sink.contramap(f)
sink.mapError(f)
sink.flatMap(f)
sink ~ otherSink
sink.?
sink.raceBoth(other)
sink.orElse(other)
sink.repeat
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val request1: Sink[Err, Byte,
Byte, Req1] = …
val request2: Sink[Err, Byte,
Byte, Req2] = …
val request:
Sink[Err, Byte,
Byte, Either[Req1, Req2]] =
request1 orElse request2
sink.map(f)
sink.contramap(f)
sink.mapError(f)
sink.flatMap(f)
sink ~ otherSink
sink.?
sink.raceBoth(other)
sink.orElse(other)
sink.repeat
Sink
PERFORMANCEMOTIVATION API DESIGN SUMMARY
val parseUser: Sink[Err, Byte,
Byte, Person] = …
val parsePeople: Sink[Err, Byte,
Byte, List[Person]] =
parseUser repeat
sink.map(f)
sink.contramap(f)
sink.mapError(f)
sink.flatMap(f)
sink ~ otherSink
sink.?
sink.raceBoth(other)
sink.orElse(other)
sink.repeat
3 How Fast Is Scalaz Stream
PERFORMANCE
Filter · Map · Sum
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Start with stream of integers (chunked)
Filter for only even integers
Convert to longs
Sum all integers
Filter · Map · Sum
PERFORMANCEMOTIVATION API DESIGN SUMMARY
def akkaChunkFilterMapSum = {
val chunks = (1 to chunkCount).flatMap(i =>
Array.fill(chunkSize)(i))
val program = AkkaSource
.fromIterator(() => chunks.iterator)
.filter(_ % 2 == 0)
.map(_.toLong)
.toMat(AkkaSink.fold(0L)(_ + _))(Keep.right)
Await.result(program.run, Duration.Inf)
}
Filter · Map · Sum
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Filter · Map · Sum
PERFORMANCEMOTIVATION API DESIGN SUMMARY
def fs2ChunkFilterMapSum = {
val chunks = (1 to chunkCount).map(i =>
FS2Chunk.array(Array.fill(chunkSize)(i)))
val stream = FS2Stream(chunks: _*)
.flatMap(FS2Stream.chunk(_))
.filter(_ % 2 == 0)
.map(_.toLong)
.covary[CatsIO]
.compile
.fold(0L)(_ + _)
stream.unsafeRunSync
}
Filter · Map · Sum
PERFORMANCEMOTIVATION API DESIGN SUMMARY
FS2
Filter · Map · Sum
PERFORMANCEMOTIVATION API DESIGN SUMMARY
def scalazChunkFilterMapSum = {
val chunks = (1 to chunkCount).map(i =>
Chunk.fromArray(Array.fill(chunkSize)(i)))
val stream = StreamChunk
.fromChunks(chunks: _*)
.filter(_ % 2 == 0)
.map(_.toLong)
val sink = Sink.foldLeft(0L)((s, c) => c.foldl(s)(_ + _))
unsafeRun(stream.run(sink))
}
Filter · Map · Sum
PERFORMANCEMOTIVATION API DESIGN SUMMARY
FS2
CSV Tokenize
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Start with stream of characters (chunked)
Identify CSV separators and columns
Convert character stream into token stream
CSV Tokenize
PERFORMANCEMOTIVATION API DESIGN SUMMARY
def akkaCsvTokenize() = {
val chunks = genCsvChunks
val program = AkkaSource
.fromIterator(() => chunks.flatten.iterator)
.scan((Vector.empty[Char], Vector.empty[CSV.Token])) {
case ((acc, _), char) =>
if (char == CSV.ColumnSep) {
Vector.empty[Char] ->
((if (acc.length > 0)
Vector(CSV.Column(acc.mkString))
else Vector.empty[CSV.Token]) ++
Vector(CSV.NewCol))
} else if (char == CSV.RowSep) {
Vector.empty[Char] ->
((if (acc.length > 0)
Vector(CSV.Column(acc.mkString))
else Vector.empty[CSV.Token]) ++
Vector(CSV.NewCol))
} else (acc :+ char) -> Vector.empty[CSV.Token]
}
.flatMapConcat(t => AkkaSource(t._2))
.toMat(AkkaSink.ignore)(Keep.right)
Await.result(program.run, Duration.Inf)
}
CSV Tokenize
PERFORMANCEMOTIVATION API DESIGN SUMMARY
CSV Tokenize
PERFORMANCEMOTIVATION API DESIGN SUMMARY
def fs2CsvTokenize() = {
val chunks = genCsvChunks.map(FS2Chunk.array(_))
val stream = FS2Stream(chunks: _*)
.flatMap(FS2Stream.chunk(_))
.scan((Vector.empty[Char], Vector.empty[CSV.Token])) {
case ((acc, _), char) =>
if (char == CSV.ColumnSep) {
Vector.empty[Char] ->
((if (acc.length > 0)
Vector(CSV.Column(acc.mkString))
else Vector.empty[CSV.Token]) ++
Vector(CSV.NewCol))
} else if (char == CSV.RowSep) {
Vector.empty[Char] ->
((if (acc.length > 0)
Vector(CSV.Column(acc.mkString))
else Vector.empty[CSV.Token]) ++
Vector(CSV.NewCol))
} else (acc :+ char) -> Vector.empty[CSV.Token]
}
.flatMap(t => FS2Stream(t._2))
.covary[CatsIO]
.compile
.drain
stream.unsafeRunSync
}
CSV Tokenize
PERFORMANCEMOTIVATION API DESIGN SUMMARY
FS2
CSV Tokenize
PERFORMANCEMOTIVATION API DESIGN SUMMARY
def scalazCsvTokenize() = {
val chunks = genCsvChunks.map(Chunk.fromArray(_))
val stream = ChunkedStream
.fromChunks(chunks: _*)
.scan[Vector[Char], Chunk[CSV.Token]](Vector.empty[Char]) {
case (acc, char) =>
if (char == CSV.ColumnSep) {
Vector.empty[Char] ->
((if (acc.length > 0)
Chunk(CSV.Column(acc.mkString))
else Chunk.empty) ++
Chunk(CSV.NewCol))
} else if (char == CSV.RowSep) {
Vector.empty[Char] ->
((if (acc.length > 0)
Chunk(CSV.Column(acc.mkString))
else Chunk.empty) ++
Chunk(CSV.NewCol))
} else (acc :+ char) -> Chunk.empty
}
.mapConcat(identity(_))
unsafeRun(stream.run(Sink.drain))
}
CSV Tokenize
PERFORMANCEMOTIVATION API DESIGN SUMMARY
FS2
4 The Inner Workings of Scalaz Stream
DESIGN
BUILD YOUR
OWN STREAM
Initial Encoding
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Initial Encoding
sealed trait Stream[+A] Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Initial Encoding
sealed trait Stream[+A]
case class Emit[A](a: A) extends Stream[A]
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Initial Encoding
sealed trait Stream[+A]
case class Emit[A](a: A) extends Stream[A]
case class Map[A0, A](s: Stream[A0],
f: A0 => A) extends Stream[A]
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Initial Encoding
sealed trait Stream[+A]
case class Emit[A](a: A) extends Stream[A]
case class Map[A0, A](s: Stream[A0],
f: A0 => A) extends Stream[A]
case class Join[A](s: Stream[Stream[A]])
extends Stream[A]
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Initial Encoding
sealed trait Stream[+A]
case class Emit[A](a: A) extends Stream[A]
case class Map[A0, A](s: Stream[A0],
f: A0 => A) extends Stream[A]
case class Join[A](s: Stream[Stream[A]])
extends Stream[A]
case class Merge[A](l: Stream[A],
r: Stream[A]) extends Stream[A]
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Initial Encoding
sealed trait Stream[+A]
case class Emit[A](a: A) extends Stream[A]
case class Map[A0, A](s: Stream[A0],
f: A0 => A) extends Stream[A]
case class Join[A](s: Stream[Stream[A]])
extends Stream[A]
case class Merge[A](l: Stream[A],
r: Stream[A]) extends Stream[A]
case class Fold[A0, A](a: A,
s: Stream[A0],
f: (A, A0) => A) extends Stream[A]
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Initial Encoding
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
sealed trait Stream[+A]
case class Emit[A](a: A) extends Stream[A]
case class Map[A0, A](s: Stream[A0],
f: A0 => A) extends Stream[A]
case class Join[A](s: Stream[Stream[A]])
extends Stream[A]
case class Merge[A](l: Stream[A],
r: Stream[A]) extends Stream[A]
case class Fold[A0, A](a: A,
s: Stream[A0],
f: (A, A0) => A) extends Stream[A]
case class Effect[A](io: IO[A]) extends Stream[A]
Initial Encoding
def drain[A](s: Stream[A]): IO[Unit] =
s match {
case Emit(a) => …
...
}
Interpreter
PERFORMANCEMOTIVATION API DESIGN SUMMARY
BUILD YOUR
OWN STREAM
Final Encoding
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Final Encoding
case class Stream[+A](
run: (A => IO[Unit]) => IO[Unit])
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Final Encoding
case class Stream[+A](
run: (A => IO[Unit]) => IO[Unit])
object Stream {
def emit[A](a: => A): Stream[A] =
Stream[A](read => read(a))
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Final Encoding
case class Stream[+A](
run: (A => IO[Unit]) => IO[Unit]) {
def map[B](f: A => B): Stream[B] =
Stream(r => run(r.compose(f)))
}
object Stream {
def emit[A](a: => A): Stream[A] =
Stream[A](read => read(a))
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Final Encoding
case class Stream[+A](
run: (A => IO[Unit]) => IO[Unit]) {
def map[B](f: A => B): Stream[B] =
Stream(r => run(r.compose(f)))
}
object Stream {
def emit[A](a: => A): Stream[A] =
Stream[A](read => read(a))
def join[A](s: Stream[Stream[A]]) =
Stream(r => s.run(sa => sa.run(r)))
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Final Encoding
case class Stream[+A](
run: (A => IO[Unit]) => IO[Unit]) {
def map[B](f: A => B): Stream[B] =
Stream(r => run(r.compose(f)))
def merge[A1 >: A](that: Stream[A1]) =
Stream[A1](r =>
run(r).par(that.run(r)).void)
}
object Stream {
def emit[A](a: => A): Stream[A] =
Stream[A](read => read(a))
def join[A](s: Stream[Stream[A]]) =
Stream(r => s.run(sa => sa.run(r)))
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Final Encoding
case class Stream[+A](
run: (A => IO[Unit]) => IO[Unit]) {
def map[B](f: A => B): Stream[B] =
Stream(r => run(r.compose(f)))
def merge[A1 >: A](that: Stream[A1]) =
Stream[A1](r =>
run(r).par(that.run(r)).void)
def fold[S](s: S)(f: (S, A) => S) = ...
}
object Stream {
def emit[A](a: => A): Stream[A] =
Stream[A](read => read(a))
def join[A](s: Stream[Stream[A]]) =
Stream(r => s.run(sa => sa.run(r)))
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Final Encoding
case class Stream[+A](
run: (A => IO[Unit]) => IO[Unit]) {
def map[B](f: A => B): Stream[B] =
Stream(r => run(r.compose(f)))
def merge[A1 >: A](that: Stream[A1]) =
Stream[A1](r =>
run(r).par(that.run(r)).void)
def fold[S](s: S)(f: (S, A) => S) = ...
}
object Stream {
def emit[A](a: => A): Stream[A] =
Stream[A](read => read(a))
def join[A](s: Stream[Stream[A]]) =
Stream(r => s.run(sa => sa.run(r)))
def liftIO[A](io: IO[A]): Stream[A] =
Stream[A](io.flatMap(_))
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Type
Emission
Mapping
Joining
Merging
Folding
Effect
...
Final
Flexible
Lookahead optimizations
Ad Hoc
Non-uniform
Initial
Initial vs Final
✓
✓
𐄂
PERFORMANCEMOTIVATION API DESIGN SUMMARY
𐄂
Inflexible
No lookahead optimizations
Structured
Uniform
𐄂
𐄂
✓
✓
Constant space processing
Discovered in 2003
Aggressively lazy
ITERATEES
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Final encoding-friendly
Automatically leak-free
“Left folds”
Iteratees 101
sealed trait Iteratee[-A, +B]
case class More[A, B](f: A => Iteratee[A, B]) extends Iteratee[A, B]
case class Done[B](value: B) extends Iteratee[Any, B]
sealed trait Enumerator[+A] { self =>
def run[A1 >: A, B](iteratee: Iteratee[A1, B]): IO[B]
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
sealed trait Enumerator[+A] { self =>
final def run[A1 >: A, B](iteratee: Iteratee[A1, B]): IO[B] =
fold(...)(...)
def fold[B, S](s: S)(f: (S, A) => Either[S, B]): IO[B]
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
sealed trait Enumerator[+A] { self =>
final def run[A1 >: A, B](iteratee: Iteratee[A1, B]): IO[B] =
fold(...)(...)
def fold[B, S](s: S)(f: (S, A) => Either[S, B]): IO[B]
final def map[B](f0: A => B): Enumerator[B] =
new Enumerator[B] {
def fold[C, S](s: S)(f: (S, B) => Either[S, C]): IO[C] =
self.fold(s)((s, a) => f(s, f0(a)))
}
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
sealed trait Enumerator[+A] { self =>
final def run[A1 >: A, B](iteratee: Iteratee[A1, B]): IO[B] =
fold(...)(...)
def fold[B, S](s: S)(f: (S, A) => Either[S, B]): IO[B]
final def map[B](f0: A => B): Enumerator[B] =
new Enumerator[B] {
def fold[C, S](s: S)(f: (S, B) => Either[S, C]): IO[C] =
self.fold(s)((s, a) => f(s, f0(a)))
}
final def filter(f0: A => Boolean): Enumerator[A] =
new Enumerator[A] {
def fold[B, S](s: S)(f: (S, A) => Either[S, B]): IO[B] =
self.fold(s)((s, a) => if (f0(a)) f(s, a) else Left(s))
}
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
𐄂 Stack Safe𐄂 Combining 𐄂 Leftovers
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
𐄂 Stack Safe?𐄂 Combining? 𐄂 Leftovers?
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
sealed trait IterateeT[F[_], -A, +B]
case class More[F[_], A, B](f: A => IterateeT[F, A, B]) extends IterateeT[F, A, B]
case class Done[F[_], B](value: B) extends IterateeT[F, Any, B]
case class Effect[F[_], B](fx: F[B]) extends IterateeT[F, Any, B]
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
sealed trait IterateeT[F[_], -A, +B]
case class More[F[_], A, B](f: A => IterateeT[F, A, B]) extends IterateeT[F, A, B]
case class Done[F[_], B](value: B) extends IterateeT[F, Any, B]
case class Effect[F[_], B](fx: F[B]) extends IterateeT[F, Any, B]
type Parallel[F[_], A1, A2, B] =
IterateeT[IterateeT[F, A1, ?], A2, B]
def lift[F[_], A1, A2, B](it: IterateeT[F, A1, B]): Parallel[F, A1, A2, B] =
Effect[IterateeT[F, A1, ?], B](it)
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
sealed trait IterateeT[F[_], -A, +B]
case class More[F[_], A, B](f: A => IterateeT[F, A, B]) extends IterateeT[F, A, B]
case class Done[F[_], B](value: B) extends IterateeT[F, Any, B]
case class Effect[F[_], B](fx: F[B]) extends IterateeT[F, Any, B]
type Parallel[F[_], A1, A2, B] =
IterateeT[IterateeT[F, A1, ?], A2, B]
def lift[F[_], A1, A2, B](it: IterateeT[F, A1, B]): Parallel[F, A1, A2, B] =
Effect[IterateeT[F, A1, ?], B](it)
def readLeft[F[_], A1, A2]: Parallel[F, A1, A2, A1] =
lift(More(a => Done(a)))
def readRight[F[_], A1, A2]: Parallel[F, A1, A2, A2] =
More(a => Done(a))
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Iteratees 101
sealed trait IterateeT[F[_], -A, +B]
case class More[F[_], A, B](f: A => IterateeT[F, A, B]) extends IterateeT[F, A, B]
case class Done[F[_], B](value: B) extends IterateeT[F, Any, B]
case class Effect[F[_], B](fx: F[B]) extends IterateeT[F, Any, B]
type Parallel[F[_], A1, A2, B] =
IterateeT[IterateeT[F, A1, ?], A2, B]
def lift[F[_], A1, A2, B](it: IterateeT[F, A1, B]): Parallel[F, A1, A2, B] =
Effect[IterateeT[F, A1, ?], B](it)
def readLeft[F[_], A1, A2]: Parallel[F, A1, A2, A1] =
lift(More(a => Done(a)))
def readRight[F[_], A1, A2]: Parallel[F, A1, A2, A2] =
More(a => Done(a))
PERFORMANCEMOTIVATION API DESIGN SUMMARY
ZIO Stream
trait Stream[E, A] {
def fold[S](s: S)(f: (S, A) => IO[E, Step[S]]): IO[E, Step[S]]
}
object Stream {
sealed trait Step[S]
object Step {
case class Stop[S](value: S) extends Step[S]
case class Cont[S](value: S) extends Step[S]
}
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
ZIO Stream
trait Stream[E, A] {
def fold[S](s: S)(f: (S, A) => IO[E, Step[S]]): IO[E, Step[S]]
}
object Stream {
sealed trait Step[S]
object Step {
case class Stop[S](value: S) extends Step[S]
case class Cont[S](value: S) extends Step[S]
}
}
Continuation Monad!
PERFORMANCEMOTIVATION API DESIGN SUMMARY
ZIO Stream
trait Stream[E, A] {
def fold[S](s: S)(f: (S, A) => IO[E, Step[S]]): IO[E, Step[S]]
def toQueue(capacity: Int = 1): Managed[Nothing, Queue[Take[E, A]]] =
for {
queue <- Managed.liftIO(Queue.bounded[Take[E, A]](capacity))
offerVal = (a: A) => queue.offer(Take.Value(a)).void
offerErr = (e: E) => queue.offer(Take.Fail(e))
enqueuer = (self.foreach[E, A](offerVal).catchAll(offerErr) *>
queue.offer(Take.End).forever).fork
_ <- Managed(enqueuer)(_.interrupt)
} yield queue
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
ZIO Stream
trait Stream[E, A] {
def fold[S](s: S)(f: (S, A) => IO[E, Step[S]]): IO[E, Step[S]]
def zip[B, C](that: Stream[E, B]): Stream[E, (A, B)] =
new Stream[E, (A, B)] {
type Q[A] = Queue[Take[E, A]]
def fold[S](s: S)(f: (S, (A, B)) => IO[E, Step[S]]): IO[E, Step[S]] = {
def loop(q1: Q[A], q2: Q[B], s: S): IO[E, Step[S]] =
Take.option(q1.take).seq(Take.option(q2.take)).flatMap {
case (Some(a), Some(b)) => f(s, (a, b))
case _ => IO.now(Step.Cont(s))
}
self.toQueue().use(q1 => that.toQueue().use(q2 => loop(q1, q2, s)))
}
}
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
ZIO Stream
𐄂 Stack Safe✓ Combining 𐄂 Leftovers
PERFORMANCEMOTIVATION API DESIGN SUMMARY
ZIO Stream
trait Stream[E, A] {
def fold[S](s: S)(f: (S, A) => IO[E, Step[S]]): IO[E, Step[S]]
def peel[R](sink: Sink[E, A, A, R]): Managed[E, (R, Stream[E, A])] =
...
}
Compose all leading sinks
Peel off sink (e.g. headers)
Work with stream remainder
PERFORMANCEMOTIVATION API DESIGN SUMMARY
ZIO Stream
𐄂 Stack Safe✓ Combining ✓ Leftovers
PERFORMANCEMOTIVATION API DESIGN SUMMARY
ZIO Stream
trait Stream[E, A] {
def fold[S]: IO[Nothing, (S, (S, A) => IO[E, Step[S]]) => IO[E, Step[S]]]
// trampolined: F[A => F[B]]
}
PERFORMANCEMOTIVATION API DESIGN SUMMARY
ZIO Stream
✓ Stack Safe✓ Combining ✓ Leftovers
PERFORMANCEMOTIVATION API DESIGN SUMMARY
5 Where To From Here
SUMMARY
Fusion, Queue &
complex combinators
Performance
Outstanding Work
Comprehensive test
suites and great docs
Tests & Docs
Combinators for
every reason
Combinators
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Eric Torreborre — Beautiful Folds
The FS2 team — Michael Pilquist, Fabio Labella, Pavel Chlupacek, et al
Special thanks to all people who made ZIO Stream possible.
Credits
Oleg Kiselyov — Iteratees 2003
PERFORMANCEMOTIVATION API DESIGN SUMMARY
Any questions?
Thanks!
John A. De Goes
@jdegoes - degoes.net
Itamar Ravid
@itrvd - iravid.com
PERFORMANCEMOTIVATION API DESIGN SUMMARY

Scalaz Stream: Rebirth

  • 1.
    John A. DeGoes @jdegoes - degoes.net SCALAZ STREAM REBIRTH Scale By The Bay · November 16 · San Francisco Itamar Ravid @itrvd - iravid.com
  • 2.
  • 3.
  • 4.
    1 Why Streams& Why Scalaz Stream MOTIVATION
  • 5.
    Why Streams? PERFORMANCEMOTIVATION APIDESIGN SUMMARY Infinite Input Sets Leak-Free Data Processing Incremental Computation ∞
  • 6.
    File & Socket Processing Graph Processing Reactive& Dataflow Akka Streams FS2 Scalaz ReactiveGraphJet Why ZIO Stream? PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 7.
    Why ZIO Stream? File& Socket Processing Graph Processing Reactive & Dataflow Akka Streams FS2 Scalaz ReactiveGraphJet ZIO STREAM PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 8.
    Why ZIO Stream? PERFORMANCEMOTIVATIONAPI DESIGN SUMMARY 𐄂 Cycles𐄂 Cons 𐄂 Uncons
  • 9.
    ZIO Stream OldScalaz Stream Akka Streams FS2 Lazy ✓ 𐄂 𐄂 ✓ Leak-Free ✓ 𐄂 𐄂 On a good day Uniform ✓ NA NA 𐄂 Type Inferred ✓ 𐄂 ✓ 𐄂 Economical ✓ 𐄂 𐄂 ✓ ZIO Integration ✓ 𐄂 𐄂 𐄂 PERFORMANCEMOTIVATION API DESIGN SUMMARY Why ZIO Stream?
  • 10.
    2 What ScalazStream Looks Like API
  • 11.
    PERFORMANCEMOTIVATION API DESIGNSUMMARY AkkaStream AbruptTerminationException AbstractShape ActorAttributes ActorMaterializer ActorMaterializerSettings AmorphousShape Attributes BidiShape BindFailedException BufferOverflowException Client ClosedShape ConnectionException DelayOverflowStrategy EagerClose FanInShape FanInShape10 FanInShape11 FanInShape12 FanInShape13 FanInShape14 FanInShape15 FanInShape16 FanInShape17 FanInShape18 FanInShape19 FanInShape1N FanInShape2 FanInShape20 FanInShape21 FanInShape22 FanInShape3 FanInShape4 FanInShape5 FanInShape6 FanInShape7 FanInShape8 FanInShape9 FanOutShape FanOutShape10 FanOutShape11 FanOutShape12 FanOutShape13 FanOutShape14 FanOutShape15 FanOutShape16 FanOutShape17 FanOutShape18 FanOutShape19 FanOutShape2 FanOutShape20 FanOutShape21 FanOutShape22 FanOutShape3 FanOutShape4 FanOutShape5 FanOutShape6 FanOutShape7 FanOutShape8 FanOutShape9 FlowMonitor FlowMonitorState FlowShape Fusing Graph IgnoreBoth IgnoreCancel IgnoreComplete Inlet InPort IOResult KillSwitch KillSwitches MaterializationContext MaterializationException Materializer MaterializerLoggingProvider Outlet OutPort OverflowStrategy QueueOfferResult RateExceededException Server Shape SharedKillSwitch SinkShape SourceShape StreamLimitReachedException StreamSubscriptionTimeoutSettings StreamSubscriptionTimeoutTerminationMode StreamTcpException SubstreamCancelStrategy Supervision ThrottleMode TLSClientAuth TLSClosing TLSProtocol TLSRole UniformFanInShape UniformFanOutShape UniqueKillSwitch
  • 12.
    PERFORMANCEMOTIVATION API DESIGNSUMMARY AkkaStream AbruptTerminationException AbstractShape ActorAttributes ActorMaterializer ActorMaterializerSettings AmorphousShape Attributes BidiShape BindFailedException BufferOverflowException Client ClosedShape ConnectionException DelayOverflowStrategy EagerClose FanInShape FanInShape10 FanInShape11 FanInShape12 FanInShape13 FanInShape14 FanInShape15 FanInShape16 FanInShape17 FanInShape18 FanInShape19 FanInShape1N FanInShape2 FanInShape20 FanInShape21 FanInShape22 FanInShape3 FanInShape4 FanInShape5 FanInShape6 FanInShape7 FanInShape8 FanInShape9 FanOutShape FanOutShape10 FanOutShape11 FanOutShape12 FanOutShape13 FanOutShape14 FanOutShape15 FanOutShape16 FanOutShape17 FanOutShape18 FanOutShape19 FanOutShape2 FanOutShape20 FanOutShape21 FanOutShape22 FanOutShape3 FanOutShape4 FanOutShape5 FanOutShape6 FanOutShape7 FanOutShape8 FanOutShape9 FlowMonitor FlowMonitorState FlowShape Fusing Graph IgnoreBoth IgnoreCancel IgnoreComplete Inlet InPort IOResult KillSwitch KillSwitches MaterializationContext MaterializationException Materializer MaterializerLoggingProvider Outlet OutPort OverflowStrategy QueueOfferResult RateExceededException Server Shape SharedKillSwitch SinkShape SourceShape StreamLimitReachedException StreamSubscriptionTimeoutSettings StreamSubscriptionTimeoutTerminationMode StreamTcpException SubstreamCancelStrategy Supervision ThrottleMode TLSClientAuth TLSClosing TLSProtocol TLSRole UniformFanInShape UniformFanOutShape UniqueKillSwitch
  • 13.
    Stream Sink PERFORMANCEMOTIVATION APIDESIGN SUMMARY ZIO STREAM
  • 14.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY Stream[E, A] May fail with a checked error of type E May effectfully produce zero or more values of type A
  • 15.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY Monoid append zero Applicative map point ap Monad bind Bifunctor leftMap bimap Alternative alt empty
  • 16.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY Stream.empty Stream.point(v) Stream.fromIterable(list) Stream.liftIO(io) Stream.fromQueue(queue) Stream.unfold(s)(f) Stream.unfoldM(s)(f) Stream.bracket(open)(close)(read) val empty: Stream[Nothing, Nothing] = Stream.empty
  • 17.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val singleton: Stream[Nothing, Int] = Stream.point(42) Stream.empty Stream.point(v) Stream.fromIterable(list) Stream.liftIO(io) Stream.fromQueue(queue) Stream.unfold(s)(f) Stream.unfoldM(s)(f) Stream.bracket(open)(close)(read)
  • 18.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val hundred: Stream[Nothing, Int] = Stream.fromIterable(0 to 100) Stream.empty Stream.point(v) Stream.fromIterable(list) Stream.liftIO(io) Stream.fromQueue(queue) Stream.unfold(s)(f) Stream.unfoldM(s)(f) Stream.bracket(open)(close)(read)
  • 19.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val userIn: Stream[IOException, String] = Stream.liftIO(getStrLn) Stream.empty Stream.point(v) Stream.fromIterable(list) Stream.liftIO(io) Stream.fromQueue(queue) Stream.unfold(s)(f) Stream.unfoldM(s)(f) Stream.bracket(open)(close)(read)
  • 20.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val intQueue: Queue[Int] = ... val intStream: Stream[Nothing, Int] = Stream.fromQueue(intQueue) Stream.empty Stream.point(v) Stream.fromIterable(list) Stream.liftIO(io) Stream.fromQueue(queue) Stream.unfold(s)(f) Stream.unfoldM(s)(f) Stream.bracket(open)(close)(read)
  • 21.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val integers: Stream[Nothing, Int] = Stream.unfold(0)(i => (i, i + 1)) Stream.empty Stream.point(v) Stream.fromIterable(list) Stream.liftIO(io) Stream.fromQueue(queue) Stream.unfold(s)(f) Stream.unfoldM(s)(f) Stream.bracket(open)(close)(read)
  • 22.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val latency: Stream[Nothing, Long] = Stream.unfoldM(0 -> 0) { case (n, total) => api.measureLatency(l => (n + 1) -> (total + n)) } Stream.empty Stream.point(v) Stream.fromIterable(list) Stream.liftIO(io) Stream.fromQueue(queue) Stream.unfold(s)(f) Stream.unfoldM(s)(f) Stream.bracket(open)(close)(read)
  • 23.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val fileBytes: Stream[IOException, Byte] = Stream.bracket(openFile)(closeFile(_)) { file => file.readByte.attempt.toOption } Stream.empty Stream.point(v) Stream.fromIterable(list) Stream.liftIO(io) Stream.fromQueue(queue) Stream.unfold(s)(f) Stream.unfoldM(s)(f) Stream.bracket(open)(close)(read)
  • 24.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val integers: Stream[Nothing, Int] = Stream.unfold(0)(i => (i, i + 1)) val integerDigits: Stream[Nothing, Int] = integers.map(_.toString).map(_.length) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 25.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val users: Stream[Exception, User] = ... val usersWithProfile: Stream[Exception, (User, Profile)] = for { user <- users prof <- Stream.liftIO(getProfile(user.id)) } yield (user, prof) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 26.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val usersWithProfile: Stream[Exception, (User, Profile)] = … val targetUsers: Stream[Exception, User] = usersWithProfile.filter(user => user.age >= 18 && user.age <= 32 && user.city == "San Francisco" && user.interests.contains("technology")) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 27.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY type File = Stream[IOException, Report] val monthlyFiles: List[File] = … val allFiles = monthlyFiles.foldLeft[File] (Stream.empty)(_ ++ _) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 28.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val stream: Stream[IOException, Chunk[Byte]] = Stream.bracket(openFile)( closeFile)(readChunk) val usersJ: Stream[IOException, Json] = stream.transduce(toJson) .filter(isUserRecord) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 29.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val integers: Stream[Nothing, Int] = Stream.unfold(0)(i => (i, i + 1)) val averages: Stream[Nothing, Int] = integers.scan(0 -> 0) { case ((sum, count), int) => (sum + int, count + 1) -> if (count == 0) Int.MaxValue else sum / count } stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 30.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val integers: Stream[Nothing, Int] = Stream.unfold(0)(i => (i, i + 1)) val first100Evens: Stream[Nothing, Int] = integers.filter(_ % 2 == 0).take(100) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 31.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val integers: Stream[Nothing, Int] = Stream.unfold(0)(i => (i, i + 1)) val evensUnder100: Stream[Nothing, Int] = integers.filter(_ % 2 == 0) .takeWhile(_ < 100) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 32.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val integers: Stream[Nothing, Int] = Stream.unfold(0)(i => (i, i + 1)) val evensAfter100: Stream[Nothing, Int] = integers.filter(_ % 2 == 0).drop(100) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 33.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val integers: Stream[Nothing, Int] = Stream.unfold(0)(i => (i, i + 1)) val evensOver100: Stream[Nothing, Int] = integers.filter(_ % 2 == 0) .dropWhile(_ < 100) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 34.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val inputs: Stream[IOException, String] = Stream.liftIO(getStrLn).forever val echo: IO[IOException, Unit] = inputs.foreach(putStrLn) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 35.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val integers: Stream[Nothing, Int] = Stream.unfold(0)(i => (i, i + 1)) val users: Stream[Exception, User] = … val usersWithId: Stream[Exception, (Int, User)] = integers.zip(users) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 36.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val stream1: Stream[IOException, JSON] = kafka.stream("events1") val stream2: Stream[IOException, JSON] = kafka.stream("events2") val both: Stream[IOException, JSON] = stream1.merge(stream2) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 37.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY val file1: Stream[IOException, User] = readFile("file1.data") .transduce(toUser) val file2: Stream[IOException, User] = readFile("file2.data") .transduce(toUser) val fastest: Stream[IOException, User] = file1.joinWith(file2)(_ race _) stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 38.
    Stream PERFORMANCEMOTIVATION API DESIGNSUMMARY type StreamIO[A] = Stream[IOException, A] val bytes: StreamIO[Byte] = … val headersAndContent: Managed[IOException, (ContentType, StreamIO[Byte])] = bytes.peel(parseContentType) headersAndContent.use { case (ContentType(Json), content) => content.transduce(toJson)... case (ContentType(Xml), content) => content.transduce(toXml)... } stream.map(f) stream.flatMap(f) stream.filter(f) stream1 ++ stream2 stream.transduce(what) stream.scan(z)(f) stream.take(n) stream.takeWhile(f) stream.drop(n) stream.dropWhile(f) stream.foreach(f) stream1.zip(stream2) stream1.merge(stream2) stream1.joinWith(stream2)(f) stream.peel(what)
  • 39.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY Sink[E, A0, A, B] May fail with a checked error of type E May effectfully produce a result of type B Consumes values of type A May possibly output a remainder of type A0
  • 40.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY trait Stream[E, A] { def run[R](sink: Sink[E, A, A, R]): IO[E, R] } myStream.run(Sink.drain) // IO[E, Unit] myStream.run(decodeRequest) // IO[E, Request] myStream.run(Sink.collect) // IO[E, List[A]]
  • 41.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY Monoid append zero Applicative map point ap Monad bind Bifunctor leftMap bimap Alternative alt empty Category id compose Profunctor lmap rmap dimap Strong first second Choice left right
  • 42.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val answer: Sink[Nothing, Nothing, Any, Int] = Sink.point(42) Sink.point(value) Sink.liftIO(ioValue) Sink.lift(f) Sink.fail(e) Sink.await Sink.drain Sink.collect Sink.fold(init)(step)
  • 43.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val lifted: Sink[Throwable, Nothing, Any, Array[Byte]] = Sink.liftIO(IO.syncThrowable(dangerousOp)) Sink.point(value) Sink.liftIO(ioValue) Sink.lift(f) Sink.fail(e) Sink.await Sink.drain Sink.collect Sink.fold(init)(step)
  • 44.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val squared: Sink[Unit, Nothing, Int, Int] = Sink.lift(i => i * i) Sink.point(value) Sink.liftIO(ioValue) Sink.lift(f) Sink.fail(e) Sink.await Sink.drain Sink.collect Sink.fold(init)(step)
  • 45.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val failure: Sink[DomainError, Nothing, Any, Nothing] = Sink.fail(BadOperation) Sink.point(value) Sink.liftIO(ioValue) Sink.lift(f) Sink.fail(e) Sink.await Sink.drain Sink.collect Sink.fold(init)(step)
  • 46.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val json: Sink[Unit, Nothing, Json, Json] = Sink.await Sink.point(value) Sink.liftIO(ioValue) Sink.lift(f) Sink.fail(e) Sink.await Sink.drain Sink.collect Sink.fold(init)(step)
  • 47.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val drain: Sink[Nothing, Nothing, Any, Unit] = Sink.drain Sink.point(value) Sink.liftIO(ioValue) Sink.lift(f) Sink.fail(e) Sink.await Sink.drain Sink.collect Sink.fold(init)(step)
  • 48.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val collected: Sink[Nothing, Nothing, Json, List[Json]] = Sink.collect[Json] Sink.point(value) Sink.liftIO(ioValue) Sink.lift(f) Sink.fail(e) Sink.await Sink.drain Sink.collect Sink.fold(init)(step)
  • 49.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val parser: Sink[Nothing, Char, Char, Either[Error, Int]] = Sink.fold((ParserState.initial, List[Int]()) { case (ParserState.Digits, c) if c.isDigit => // ... } Sink.point(value) Sink.liftIO(ioValue) Sink.lift(f) Sink.fail(e) Sink.await Sink.drain Sink.collect Sink.fold(init)(step)
  • 50.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val digits: Sink[Nothing, Char, Char, List[Char]] = Sink.readWhile[Char](_.isDigit) val number: Sink[Nothing, Char, Char, Int] = Sink.map(_.mkString.toInt) sink.map(f) sink.contramap(f) sink.mapError(f) sink.flatMap(f) sink ~ otherSink sink.? sink.raceBoth(other) sink.orElse(other) sink.repeat
  • 51.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val summing: Sink[Nothing, Int, Int, Int] = Sink.fold(0)(_ + _) case class Person(name: String, salary: Int) val salarySum: Sink[Nothing, Int, Person, Int] = Sink.contramap[Person](_.salary) sink.map(f) sink.contramap(f) sink.mapError(f) sink.flatMap(f) sink ~ otherSink sink.? sink.raceBoth(other) sink.orElse(other) sink.repeat
  • 52.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val err: Sink[DomainError, Nothing, Any, Nothing] = Sink.fail(DomainError) val errs: Sink[AppError, Nothing, Any, Nothing] = Sink.mapError(AppError(_)) sink.map(f) sink.contramap(f) sink.mapError(f) sink.flatMap(f) sink ~ otherSink sink.? sink.raceBoth(other) sink.orElse(other) sink.repeat
  • 53.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val sumOfConsecutive: Sink[Nothing, Nothing, Int, Int] = for { a <- Sink.await b <- Sink.await } yield a + b sink.map(f) sink.contramap(f) sink.mapError(f) sink.flatMap(f) sink ~ otherSink sink.? sink.raceBoth(other) sink.orElse(other) sink.repeat
  • 54.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val parseHeader: Sink[Err, Byte, Byte, Header] = … val parseBody: Sink[Err, Byte, Byte, Body] = … val parseHeaderAndBody: Sink[Err, Byte, Byte, (Header, Body)] = parseHeader ~ parseBody sink.map(f) sink.contramap(f) sink.mapError(f) sink.flatMap(f) sink ~ otherSink sink.? sink.raceBoth(other) sink.orElse(other) sink.repeat
  • 55.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val parseHeader: Sink[Err, Byte, Byte, Header] = … val maybeParseHeader: Sink[Nothing, Byte, Byte, Option[Header]] = parseHeader ? sink.map(f) sink.contramap(f) sink.mapError(f) sink.flatMap(f) sink ~ otherSink sink.? sink.raceBoth(other) sink.orElse(other) sink.repeat
  • 56.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val request1: Sink[Err, Byte, Byte, Req1] = … val request2: Sink[Err, Byte, Byte, Req2] = … val request: Sink[Err, Byte, Byte, Either[Req1, Req2]] = request1 raceBoth request2 sink.map(f) sink.contramap(f) sink.mapError(f) sink.flatMap(f) sink ~ otherSink sink.? sink.raceBoth(other) sink.orElse(other) sink.repeat
  • 57.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val request1: Sink[Err, Byte, Byte, Req1] = … val request2: Sink[Err, Byte, Byte, Req2] = … val request: Sink[Err, Byte, Byte, Either[Req1, Req2]] = request1 orElse request2 sink.map(f) sink.contramap(f) sink.mapError(f) sink.flatMap(f) sink ~ otherSink sink.? sink.raceBoth(other) sink.orElse(other) sink.repeat
  • 58.
    Sink PERFORMANCEMOTIVATION API DESIGNSUMMARY val parseUser: Sink[Err, Byte, Byte, Person] = … val parsePeople: Sink[Err, Byte, Byte, List[Person]] = parseUser repeat sink.map(f) sink.contramap(f) sink.mapError(f) sink.flatMap(f) sink ~ otherSink sink.? sink.raceBoth(other) sink.orElse(other) sink.repeat
  • 59.
    3 How FastIs Scalaz Stream PERFORMANCE
  • 60.
    Filter · Map· Sum PERFORMANCEMOTIVATION API DESIGN SUMMARY Start with stream of integers (chunked) Filter for only even integers Convert to longs Sum all integers
  • 61.
    Filter · Map· Sum PERFORMANCEMOTIVATION API DESIGN SUMMARY def akkaChunkFilterMapSum = { val chunks = (1 to chunkCount).flatMap(i => Array.fill(chunkSize)(i)) val program = AkkaSource .fromIterator(() => chunks.iterator) .filter(_ % 2 == 0) .map(_.toLong) .toMat(AkkaSink.fold(0L)(_ + _))(Keep.right) Await.result(program.run, Duration.Inf) }
  • 62.
    Filter · Map· Sum PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 63.
    Filter · Map· Sum PERFORMANCEMOTIVATION API DESIGN SUMMARY def fs2ChunkFilterMapSum = { val chunks = (1 to chunkCount).map(i => FS2Chunk.array(Array.fill(chunkSize)(i))) val stream = FS2Stream(chunks: _*) .flatMap(FS2Stream.chunk(_)) .filter(_ % 2 == 0) .map(_.toLong) .covary[CatsIO] .compile .fold(0L)(_ + _) stream.unsafeRunSync }
  • 64.
    Filter · Map· Sum PERFORMANCEMOTIVATION API DESIGN SUMMARY FS2
  • 65.
    Filter · Map· Sum PERFORMANCEMOTIVATION API DESIGN SUMMARY def scalazChunkFilterMapSum = { val chunks = (1 to chunkCount).map(i => Chunk.fromArray(Array.fill(chunkSize)(i))) val stream = StreamChunk .fromChunks(chunks: _*) .filter(_ % 2 == 0) .map(_.toLong) val sink = Sink.foldLeft(0L)((s, c) => c.foldl(s)(_ + _)) unsafeRun(stream.run(sink)) }
  • 66.
    Filter · Map· Sum PERFORMANCEMOTIVATION API DESIGN SUMMARY FS2
  • 67.
    CSV Tokenize PERFORMANCEMOTIVATION APIDESIGN SUMMARY Start with stream of characters (chunked) Identify CSV separators and columns Convert character stream into token stream
  • 68.
    CSV Tokenize PERFORMANCEMOTIVATION APIDESIGN SUMMARY def akkaCsvTokenize() = { val chunks = genCsvChunks val program = AkkaSource .fromIterator(() => chunks.flatten.iterator) .scan((Vector.empty[Char], Vector.empty[CSV.Token])) { case ((acc, _), char) => if (char == CSV.ColumnSep) { Vector.empty[Char] -> ((if (acc.length > 0) Vector(CSV.Column(acc.mkString)) else Vector.empty[CSV.Token]) ++ Vector(CSV.NewCol)) } else if (char == CSV.RowSep) { Vector.empty[Char] -> ((if (acc.length > 0) Vector(CSV.Column(acc.mkString)) else Vector.empty[CSV.Token]) ++ Vector(CSV.NewCol)) } else (acc :+ char) -> Vector.empty[CSV.Token] } .flatMapConcat(t => AkkaSource(t._2)) .toMat(AkkaSink.ignore)(Keep.right) Await.result(program.run, Duration.Inf) }
  • 69.
  • 70.
    CSV Tokenize PERFORMANCEMOTIVATION APIDESIGN SUMMARY def fs2CsvTokenize() = { val chunks = genCsvChunks.map(FS2Chunk.array(_)) val stream = FS2Stream(chunks: _*) .flatMap(FS2Stream.chunk(_)) .scan((Vector.empty[Char], Vector.empty[CSV.Token])) { case ((acc, _), char) => if (char == CSV.ColumnSep) { Vector.empty[Char] -> ((if (acc.length > 0) Vector(CSV.Column(acc.mkString)) else Vector.empty[CSV.Token]) ++ Vector(CSV.NewCol)) } else if (char == CSV.RowSep) { Vector.empty[Char] -> ((if (acc.length > 0) Vector(CSV.Column(acc.mkString)) else Vector.empty[CSV.Token]) ++ Vector(CSV.NewCol)) } else (acc :+ char) -> Vector.empty[CSV.Token] } .flatMap(t => FS2Stream(t._2)) .covary[CatsIO] .compile .drain stream.unsafeRunSync }
  • 71.
  • 72.
    CSV Tokenize PERFORMANCEMOTIVATION APIDESIGN SUMMARY def scalazCsvTokenize() = { val chunks = genCsvChunks.map(Chunk.fromArray(_)) val stream = ChunkedStream .fromChunks(chunks: _*) .scan[Vector[Char], Chunk[CSV.Token]](Vector.empty[Char]) { case (acc, char) => if (char == CSV.ColumnSep) { Vector.empty[Char] -> ((if (acc.length > 0) Chunk(CSV.Column(acc.mkString)) else Chunk.empty) ++ Chunk(CSV.NewCol)) } else if (char == CSV.RowSep) { Vector.empty[Char] -> ((if (acc.length > 0) Chunk(CSV.Column(acc.mkString)) else Chunk.empty) ++ Chunk(CSV.NewCol)) } else (acc :+ char) -> Chunk.empty } .mapConcat(identity(_)) unsafeRun(stream.run(Sink.drain)) }
  • 73.
  • 74.
    4 The InnerWorkings of Scalaz Stream DESIGN
  • 75.
    BUILD YOUR OWN STREAM InitialEncoding PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 76.
    Initial Encoding sealed traitStream[+A] Type Emission Mapping Joining Merging Folding Effect ... PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 77.
    Initial Encoding sealed traitStream[+A] case class Emit[A](a: A) extends Stream[A] PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 78.
    Initial Encoding sealed traitStream[+A] case class Emit[A](a: A) extends Stream[A] case class Map[A0, A](s: Stream[A0], f: A0 => A) extends Stream[A] PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 79.
    Initial Encoding sealed traitStream[+A] case class Emit[A](a: A) extends Stream[A] case class Map[A0, A](s: Stream[A0], f: A0 => A) extends Stream[A] case class Join[A](s: Stream[Stream[A]]) extends Stream[A] PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 80.
    Initial Encoding sealed traitStream[+A] case class Emit[A](a: A) extends Stream[A] case class Map[A0, A](s: Stream[A0], f: A0 => A) extends Stream[A] case class Join[A](s: Stream[Stream[A]]) extends Stream[A] case class Merge[A](l: Stream[A], r: Stream[A]) extends Stream[A] PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 81.
    Initial Encoding sealed traitStream[+A] case class Emit[A](a: A) extends Stream[A] case class Map[A0, A](s: Stream[A0], f: A0 => A) extends Stream[A] case class Join[A](s: Stream[Stream[A]]) extends Stream[A] case class Merge[A](l: Stream[A], r: Stream[A]) extends Stream[A] case class Fold[A0, A](a: A, s: Stream[A0], f: (A, A0) => A) extends Stream[A] PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 82.
    Initial Encoding PERFORMANCEMOTIVATION APIDESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ... sealed trait Stream[+A] case class Emit[A](a: A) extends Stream[A] case class Map[A0, A](s: Stream[A0], f: A0 => A) extends Stream[A] case class Join[A](s: Stream[Stream[A]]) extends Stream[A] case class Merge[A](l: Stream[A], r: Stream[A]) extends Stream[A] case class Fold[A0, A](a: A, s: Stream[A0], f: (A, A0) => A) extends Stream[A] case class Effect[A](io: IO[A]) extends Stream[A]
  • 83.
    Initial Encoding def drain[A](s:Stream[A]): IO[Unit] = s match { case Emit(a) => … ... } Interpreter PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 84.
    BUILD YOUR OWN STREAM FinalEncoding PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 85.
    Final Encoding case classStream[+A]( run: (A => IO[Unit]) => IO[Unit]) PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 86.
    Final Encoding case classStream[+A]( run: (A => IO[Unit]) => IO[Unit]) object Stream { def emit[A](a: => A): Stream[A] = Stream[A](read => read(a)) } PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 87.
    Final Encoding case classStream[+A]( run: (A => IO[Unit]) => IO[Unit]) { def map[B](f: A => B): Stream[B] = Stream(r => run(r.compose(f))) } object Stream { def emit[A](a: => A): Stream[A] = Stream[A](read => read(a)) } PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 88.
    Final Encoding case classStream[+A]( run: (A => IO[Unit]) => IO[Unit]) { def map[B](f: A => B): Stream[B] = Stream(r => run(r.compose(f))) } object Stream { def emit[A](a: => A): Stream[A] = Stream[A](read => read(a)) def join[A](s: Stream[Stream[A]]) = Stream(r => s.run(sa => sa.run(r))) } PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 89.
    Final Encoding case classStream[+A]( run: (A => IO[Unit]) => IO[Unit]) { def map[B](f: A => B): Stream[B] = Stream(r => run(r.compose(f))) def merge[A1 >: A](that: Stream[A1]) = Stream[A1](r => run(r).par(that.run(r)).void) } object Stream { def emit[A](a: => A): Stream[A] = Stream[A](read => read(a)) def join[A](s: Stream[Stream[A]]) = Stream(r => s.run(sa => sa.run(r))) } PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 90.
    Final Encoding case classStream[+A]( run: (A => IO[Unit]) => IO[Unit]) { def map[B](f: A => B): Stream[B] = Stream(r => run(r.compose(f))) def merge[A1 >: A](that: Stream[A1]) = Stream[A1](r => run(r).par(that.run(r)).void) def fold[S](s: S)(f: (S, A) => S) = ... } object Stream { def emit[A](a: => A): Stream[A] = Stream[A](read => read(a)) def join[A](s: Stream[Stream[A]]) = Stream(r => s.run(sa => sa.run(r))) } PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 91.
    Final Encoding case classStream[+A]( run: (A => IO[Unit]) => IO[Unit]) { def map[B](f: A => B): Stream[B] = Stream(r => run(r.compose(f))) def merge[A1 >: A](that: Stream[A1]) = Stream[A1](r => run(r).par(that.run(r)).void) def fold[S](s: S)(f: (S, A) => S) = ... } object Stream { def emit[A](a: => A): Stream[A] = Stream[A](read => read(a)) def join[A](s: Stream[Stream[A]]) = Stream(r => s.run(sa => sa.run(r))) def liftIO[A](io: IO[A]): Stream[A] = Stream[A](io.flatMap(_)) } PERFORMANCEMOTIVATION API DESIGN SUMMARY Type Emission Mapping Joining Merging Folding Effect ...
  • 92.
    Final Flexible Lookahead optimizations Ad Hoc Non-uniform Initial Initialvs Final ✓ ✓ 𐄂 PERFORMANCEMOTIVATION API DESIGN SUMMARY 𐄂 Inflexible No lookahead optimizations Structured Uniform 𐄂 𐄂 ✓ ✓
  • 93.
    Constant space processing Discoveredin 2003 Aggressively lazy ITERATEES PERFORMANCEMOTIVATION API DESIGN SUMMARY Final encoding-friendly Automatically leak-free “Left folds”
  • 94.
    Iteratees 101 sealed traitIteratee[-A, +B] case class More[A, B](f: A => Iteratee[A, B]) extends Iteratee[A, B] case class Done[B](value: B) extends Iteratee[Any, B] sealed trait Enumerator[+A] { self => def run[A1 >: A, B](iteratee: Iteratee[A1, B]): IO[B] } PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 95.
    Iteratees 101 sealed traitEnumerator[+A] { self => final def run[A1 >: A, B](iteratee: Iteratee[A1, B]): IO[B] = fold(...)(...) def fold[B, S](s: S)(f: (S, A) => Either[S, B]): IO[B] } PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 96.
    Iteratees 101 sealed traitEnumerator[+A] { self => final def run[A1 >: A, B](iteratee: Iteratee[A1, B]): IO[B] = fold(...)(...) def fold[B, S](s: S)(f: (S, A) => Either[S, B]): IO[B] final def map[B](f0: A => B): Enumerator[B] = new Enumerator[B] { def fold[C, S](s: S)(f: (S, B) => Either[S, C]): IO[C] = self.fold(s)((s, a) => f(s, f0(a))) } } PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 97.
    Iteratees 101 sealed traitEnumerator[+A] { self => final def run[A1 >: A, B](iteratee: Iteratee[A1, B]): IO[B] = fold(...)(...) def fold[B, S](s: S)(f: (S, A) => Either[S, B]): IO[B] final def map[B](f0: A => B): Enumerator[B] = new Enumerator[B] { def fold[C, S](s: S)(f: (S, B) => Either[S, C]): IO[C] = self.fold(s)((s, a) => f(s, f0(a))) } final def filter(f0: A => Boolean): Enumerator[A] = new Enumerator[A] { def fold[B, S](s: S)(f: (S, A) => Either[S, B]): IO[B] = self.fold(s)((s, a) => if (f0(a)) f(s, a) else Left(s)) } } PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 98.
    Iteratees 101 𐄂 StackSafe𐄂 Combining 𐄂 Leftovers PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 99.
    Iteratees 101 𐄂 StackSafe?𐄂 Combining? 𐄂 Leftovers? PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 100.
  • 101.
  • 102.
  • 103.
    Iteratees 101 sealed traitIterateeT[F[_], -A, +B] case class More[F[_], A, B](f: A => IterateeT[F, A, B]) extends IterateeT[F, A, B] case class Done[F[_], B](value: B) extends IterateeT[F, Any, B] case class Effect[F[_], B](fx: F[B]) extends IterateeT[F, Any, B] PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 104.
    Iteratees 101 sealed traitIterateeT[F[_], -A, +B] case class More[F[_], A, B](f: A => IterateeT[F, A, B]) extends IterateeT[F, A, B] case class Done[F[_], B](value: B) extends IterateeT[F, Any, B] case class Effect[F[_], B](fx: F[B]) extends IterateeT[F, Any, B] type Parallel[F[_], A1, A2, B] = IterateeT[IterateeT[F, A1, ?], A2, B] def lift[F[_], A1, A2, B](it: IterateeT[F, A1, B]): Parallel[F, A1, A2, B] = Effect[IterateeT[F, A1, ?], B](it) PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 105.
    Iteratees 101 sealed traitIterateeT[F[_], -A, +B] case class More[F[_], A, B](f: A => IterateeT[F, A, B]) extends IterateeT[F, A, B] case class Done[F[_], B](value: B) extends IterateeT[F, Any, B] case class Effect[F[_], B](fx: F[B]) extends IterateeT[F, Any, B] type Parallel[F[_], A1, A2, B] = IterateeT[IterateeT[F, A1, ?], A2, B] def lift[F[_], A1, A2, B](it: IterateeT[F, A1, B]): Parallel[F, A1, A2, B] = Effect[IterateeT[F, A1, ?], B](it) def readLeft[F[_], A1, A2]: Parallel[F, A1, A2, A1] = lift(More(a => Done(a))) def readRight[F[_], A1, A2]: Parallel[F, A1, A2, A2] = More(a => Done(a)) PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 106.
    Iteratees 101 sealed traitIterateeT[F[_], -A, +B] case class More[F[_], A, B](f: A => IterateeT[F, A, B]) extends IterateeT[F, A, B] case class Done[F[_], B](value: B) extends IterateeT[F, Any, B] case class Effect[F[_], B](fx: F[B]) extends IterateeT[F, Any, B] type Parallel[F[_], A1, A2, B] = IterateeT[IterateeT[F, A1, ?], A2, B] def lift[F[_], A1, A2, B](it: IterateeT[F, A1, B]): Parallel[F, A1, A2, B] = Effect[IterateeT[F, A1, ?], B](it) def readLeft[F[_], A1, A2]: Parallel[F, A1, A2, A1] = lift(More(a => Done(a))) def readRight[F[_], A1, A2]: Parallel[F, A1, A2, A2] = More(a => Done(a)) PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 107.
    ZIO Stream trait Stream[E,A] { def fold[S](s: S)(f: (S, A) => IO[E, Step[S]]): IO[E, Step[S]] } object Stream { sealed trait Step[S] object Step { case class Stop[S](value: S) extends Step[S] case class Cont[S](value: S) extends Step[S] } } PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 108.
    ZIO Stream trait Stream[E,A] { def fold[S](s: S)(f: (S, A) => IO[E, Step[S]]): IO[E, Step[S]] } object Stream { sealed trait Step[S] object Step { case class Stop[S](value: S) extends Step[S] case class Cont[S](value: S) extends Step[S] } } Continuation Monad! PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 109.
    ZIO Stream trait Stream[E,A] { def fold[S](s: S)(f: (S, A) => IO[E, Step[S]]): IO[E, Step[S]] def toQueue(capacity: Int = 1): Managed[Nothing, Queue[Take[E, A]]] = for { queue <- Managed.liftIO(Queue.bounded[Take[E, A]](capacity)) offerVal = (a: A) => queue.offer(Take.Value(a)).void offerErr = (e: E) => queue.offer(Take.Fail(e)) enqueuer = (self.foreach[E, A](offerVal).catchAll(offerErr) *> queue.offer(Take.End).forever).fork _ <- Managed(enqueuer)(_.interrupt) } yield queue } PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 110.
    ZIO Stream trait Stream[E,A] { def fold[S](s: S)(f: (S, A) => IO[E, Step[S]]): IO[E, Step[S]] def zip[B, C](that: Stream[E, B]): Stream[E, (A, B)] = new Stream[E, (A, B)] { type Q[A] = Queue[Take[E, A]] def fold[S](s: S)(f: (S, (A, B)) => IO[E, Step[S]]): IO[E, Step[S]] = { def loop(q1: Q[A], q2: Q[B], s: S): IO[E, Step[S]] = Take.option(q1.take).seq(Take.option(q2.take)).flatMap { case (Some(a), Some(b)) => f(s, (a, b)) case _ => IO.now(Step.Cont(s)) } self.toQueue().use(q1 => that.toQueue().use(q2 => loop(q1, q2, s))) } } } PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 111.
    ZIO Stream 𐄂 StackSafe✓ Combining 𐄂 Leftovers PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 112.
    ZIO Stream trait Stream[E,A] { def fold[S](s: S)(f: (S, A) => IO[E, Step[S]]): IO[E, Step[S]] def peel[R](sink: Sink[E, A, A, R]): Managed[E, (R, Stream[E, A])] = ... } Compose all leading sinks Peel off sink (e.g. headers) Work with stream remainder PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 113.
    ZIO Stream 𐄂 StackSafe✓ Combining ✓ Leftovers PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 114.
    ZIO Stream trait Stream[E,A] { def fold[S]: IO[Nothing, (S, (S, A) => IO[E, Step[S]]) => IO[E, Step[S]]] // trampolined: F[A => F[B]] } PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 115.
    ZIO Stream ✓ StackSafe✓ Combining ✓ Leftovers PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 116.
    5 Where ToFrom Here SUMMARY
  • 117.
    Fusion, Queue & complexcombinators Performance Outstanding Work Comprehensive test suites and great docs Tests & Docs Combinators for every reason Combinators PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 118.
    Eric Torreborre —Beautiful Folds The FS2 team — Michael Pilquist, Fabio Labella, Pavel Chlupacek, et al Special thanks to all people who made ZIO Stream possible. Credits Oleg Kiselyov — Iteratees 2003 PERFORMANCEMOTIVATION API DESIGN SUMMARY
  • 119.
    Any questions? Thanks! John A.De Goes @jdegoes - degoes.net Itamar Ravid @itrvd - iravid.com PERFORMANCEMOTIVATION API DESIGN SUMMARY