+ All Categories
Home > Documents > A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU...

A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU...

Date post: 26-Jun-2020
Category:
Upload: others
View: 0 times
Download: 0 times
Share this document with a friend
96
A TALE OF TWO MONIX STREAMS Alexandru Nedelcu @alexelcu | alexn.org
Transcript
Page 1: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

A TALE OF TWO MONIX STREAMS

Alexandru Nedelcu@alexelcu | alexn.org

Page 2: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

WHAT IS MONIX?▸ Scala / Scala.js library

▸ For composing asynchronous programs

▸ Exposes Observable, Task, Coeval,Iterant and many concurrency primitives

▸ Typelevel (see typelevel.org)

▸ 3.0.0-M1

▸ See: monix.io

2

Page 3: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

3.0▸ Deep integration with Typelevel Cats

▸ Iterant data type for lawful pull-based streaming

▸ major improvements to Observable, Task, Coeval and CancelableFuture

3MONIX

Page 4: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIFU▸ Started on January 2, 2014, at 2:30 a.m.

▸ Developed at Eloquentix for monitoring and controlling power plants

▸ Inspired by RxJava / ReactiveX (link)

▸ Renamed to Monix on Dec 30, 2015(issue #91)

▸ Monix comes from: Monads + Rx

4MONIX

Page 5: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIFU▸ Started on January 2, 2014, at 2:30 a.m.

▸ Developed at Eloquentix for monitoring and controlling power plants

▸ Inspired by RxJava / ReactiveX (link)

▸ Renamed to Monix on Dec 30, 2015(issue #91)

▸ Monix comes from: Monads + Rx

5MONIX

Page 6: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIFU▸ Started on January 2, 2014, at 2:30 a.m.

▸ Developed at Eloquentix for monitoring and controlling power plants

▸ Inspired by RxJava / ReactiveX (link)

▸ Renamed to Monix on Dec 30, 2015(issue #91)

▸ Monix comes from: Monads + Rx

6MONIX

Page 7: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIFU▸ Started on January 2, 2014

▸ Developed at Eloquentix for monitoring and controlling power plants

▸ Inspired by RxJava / ReactiveX (link)

▸ Renamed to Monix on Dec 30, 2015(issue #91)

▸ Monix comes from: Monads + Rx

7MONIX

Page 8: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

I’M A DEVELOPER, I HAVE NO LIFE

8MONIX

Page 9: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

9MONIX

💙+ =

Page 10: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE[+A]PUSH - BASED STREAMING

Page 11: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

RX .NET - ORIGINS

▸ Reactive Extensions (also known as ReactiveX)

▸ The Observable pattern

▸ Built at Microsoft by

▸ Jeffery Van Gogh

▸ Wes Dyer

▸ Erik Meijer

▸ Bart De Smet

11

Page 12: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

RX .NET - ORIGINS

12

trait Iterator[+A] { def hasNext: Boolean def next(): A }

trait Iterable[+A] { def iterator: Iterator[A] }

Page 13: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

RX .NET - ORIGINS

13

type Iterator[+A] = () !=> Option[Either[Throwable, A]]

type Iterable[+A] = () !=> Iterator[A]

Page 14: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

RX .NET - ORIGINS

14

type Observer[-A] = Option[Either[Throwable, A]] !=> Unit

type Observable[+A] = Observer[A] !=> Unit

Page 15: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

RX .NET - ORIGINS

15

trait Observable[+A] { def subscribe(o: Observer[A]): Cancelable }

trait Observer[-A] { def onNext(elem: A): Unit def onComplete(): Unit def onError(e: Throwable): Unit }

Page 16: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

RX .NET - ORIGINS

16

Page 17: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE IS THE DUAL OF ITERABLE

Erik Meijer

MONIX 17

Page 18: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

18MONIX

▸Pure push - based

▸No protections against slow consumers

▸Unbounded buffers / throttling

▸Trivia: flatMap is aliased to mergeMap because concatMap is unsafe

RX .NET - PROBLEMS

Page 19: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

19MONIX

▸Pure push - based

▸No protections against slow consumers

▸Unbounded buffers / throttling

▸Trivia: flatMap is aliased to mergeMap because concatMap is unsafe

RX .NET - PROBLEMS

Page 20: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

20MONIX

▸Pure push - based

▸No protections against slow consumers

▸Unbounded buffers / throttling

▸Trivia: flatMap is aliased to mergeMap because concatMap is unsafe

RX .NET - PROBLEMS

Page 21: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

REACTIVE-STREAMS.ORG

21

trait Subscription { def request(n: Long): Unit def cancel(): Unit }

trait Subscriber[A] { def onSubscribe(s: Subscription): Unit

def onNext(elem: A): Unit def onComplete(): Unit def onError(e: Throwable): Unit }

Page 22: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

TOWARD THE FUTURE[A]

Page 23: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

IDEA 1: BACK-PRESURE WITH FUTURE

23

import scala.concurrent.Future

trait Observer[-A] { def onNext(elem: A): Future[Unit]

def onComplete(): Unit

def onError(e: Throwable): Unit }

Page 24: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

IDEA 2: CONSUMER DRIVEN CANCELATION

24

sealed trait Ack extends Future[Ack] { !// !!... }

object Ack { /** Signals demand for more. !*/ case object Continue extends Ack

/** Signals demand for early termination. !*/ case object Stop extends Ack }

Page 25: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

IDEA 2: CONSUMER DRIVEN CANCELATION

25

import monix.execution.Ack import scala.concurrent.Future

trait Observer[-A] { def onNext(elem: A): Future[Ack]

def onComplete(): Unit

def onError(e: Throwable): Unit }

Page 26: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

SIDE EFFECTS, W00T!26

class SumObserver(take: Int) extends Observer[Int] { private var count = 0 private var sum = 0

def onNext(elem: Int): Ack = { count += 1 sum += elem if (count < take) Continue else { onComplete() Stop } }

def onComplete() = println(s"Sum: $sum") def onError(e: Throwable) = e.printStackTrace() }

Page 27: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

OBSERVABLE IS HIGH-LEVEL27

val sum: Observable[Long] = Observable.range(0, 1000) .take(100) .map(_ * 2) .foldF

!// Actual execution sum.subscribe(result !=> { println(s"Sum: $result") Stop })

Page 28: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

val sum: Task[Long] = Observable.range(0, 1000) .take(100) .map(_ * 2) .foldL

!// Actual execution val f: CancelableFuture[Long] = sum.runAsync

OBSERVABLE IS HIGH-LEVEL28

Page 29: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

val list: Observable[Long] = Observable.range(0, 1000) .take(100) .map(_ * 2)

val consumer: Consumer[Long, Long] = Consumer.foldLeft(0L)(_ + _)

val task: Task[Long] = list.consumeWith(consumer)

OBSERVABLE IS HIGH-LEVEL29

Page 30: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

def eventsSeq(key: Long): Observable[Long] = ???

observable.flatMap { key !=> eventsSeq(key) }

def someTask(key: Long): Task[Long] = ???

observable.mapTask { key !=> someTask(key) }

def someIO(key: Long): IO[Long] = ???

observable.mapEval { key !=> someIO(key) }

OBSERVABLE IS A MONADIC TYPE30

Page 31: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

SUSPENDING SIDE EFFECTS31

def readFile(path: String): Observable[String] = Observable.suspend { !// The side effect val lines = Source.fromFile(path).getLines Observable.fromIterator(lines) }

Page 32: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

SUSPENDING SIDE EFFECTS

▸Does not need IO / Task for evaluation or suspending effects

▸Observable is IO-ish

32

Page 33: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

observable.mergeMap { key !=> eventsSeq(key) }

observable.switchMap { key !=> eventsSeq(key) }

REACTIVE W00T!33

Page 34: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

observable.throttleFirst(1.second)

observable.sample(1.second)

observable.debounce(1.second)

observable.echoOnce(1.second)

REACTIVE W00T!34

Page 35: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

observable.sampleRepeated(1.second)

observable.debounceRepeated(1.second)

observable.echoRepeated(1.second)

REACTIVE W00T!35

Page 36: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

observable .distinctUntilChanged .sample(1.second) .echoRepeated(5.seconds)

REACTIVE W00T!36

Page 37: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

observable.publishSelector { hot !=> val a = hot.filter(_ % 2 !== 0).map(_ * 2) val b = hot.filter(_ % 2 !== 1).map(_ * 3) Observable.merge(a, b) }

REACTIVE W00T!37

Page 38: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

import monix.reactive.OverflowStrategy._

observable.whileBusyBuffer( DropNewAndSignal(1000, count !=> { logger.warn(s"$count events dropped") None }))

observable.asyncBoundary(BackPressure(1000))

REACTIVE W00T!38

Page 39: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

OBSERVABLE OPTIMISATIONS

▸ Models complex state machine for eliminating asynchronous boundaries

▸ Deals with Concurrency by means of one Atomic

▸ Cache-line padding for avoiding false sharing

▸ Uses getAndSet platform intrinsics

▸ monix-execution ftw

39

FOR FLAT-MAP

Page 40: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

OBSERVABLE OPTIMISATIONS

▸ Models complex state machine for eliminating asynchronous boundaries

▸ Deals with Concurrency by means of one Atomic

▸ Cache-line padding for avoiding false sharing

▸ Uses getAndSet platform intrinsics

▸ monix-execution ftw

40

FOR FLAT-MAP

Page 41: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

OBSERVABLE OPTIMISATIONS

▸ Models complex state machine for eliminating asynchronous boundaries

▸ Deals with Concurrency by means of one Atomic

▸ Cache-line padding for avoiding false sharing

▸ Uses getAndSet platform intrinsics

▸ monix-execution ftw

41

FOR FLAT-MAP

Page 42: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

OBSERVABLE OPTIMISATIONS

▸ Using JCTools.org for non-blocking queues

▸ MPSC scenarios

▸ Consumer does not contend with producers

42

FOR MERGE-MAP / BUFFERING

Page 43: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

CONSEQUENCES

▸Best in class performance(synchronous ops have ~zero overhead, can optimise synchronous pipelines)

▸Referential Transparency(subscribe <-> unsafePerformIO)

▸ Pure API, Dirty Internals

43

Page 44: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

MONIX

CONSEQUENCES

▸Best in class performance(synchronous ops have ~zero overhead, can optimise synchronous pipelines)

▸Referential Transparency (subscribe <-> unsafePerformIO)

▸Pure API, Dirty Internals

44

Page 45: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT[F,A]PULL-BASED STREAMING

Page 46: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ARCHITECTURE IS FROZEN MUSIC

Johann Wolfgang Von Goethe

ITERANT 46

Page 47: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

DATA STRUCTURES ARE FROZEN ALGORITHMS

Jon Bentley

ITERANT 47

Page 48: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

48ITERANT

1. Freeze Algorithms into Data-Structures(Immutable)

2. Think State Machines

3. Be Lazy

FP DESIGN - KEY INSIGHTS

Page 49: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

49ITERANT

1. Freeze Algorithms into Data-Structures

2. Think State Machines(most of the time)

3. Be Lazy

FP DESIGN - KEY INSIGHTS

Page 50: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

50ITERANT

1. Freeze Algorithms into Data-Structures

2. Think State Machines

3. Be Lazy (Strict Values => Functions ;-))

FP DESIGN - KEY INSIGHTS

Page 51: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

Finite State Machine Cat

Page 52: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

LINKED LISTS

52

sealed trait List[+A]

case class Cons[+A]( head: A, tail: List[A]) extends List[A]

case object Nil extends List[Nothing]

Page 53: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

LAZY EVALUATION

53

sealed trait Iterant[A]

case class Next[A]( item: A, rest: () !=> Iterant[A]) extends Iterant[A]

case class Halt[A]( e: Option[Throwable]) extends Iterant[A]

Page 54: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

RESOURCE MANAGEMENT

54

sealed trait Iterant[A]

case class Next[A]( item: A, rest: () !=> Iterant[A], stop: () !=> Unit) extends Iterant[A]

case class Halt[A]( e: Option[Throwable]) extends Iterant[A]

Page 55: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

DEFERRING

55

sealed trait Iterant[A]

!// !!... case class Suspend[A]( rest: () !=> Iterant[A], stop: () !=> Unit) extends Iterant[A]

Page 56: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

FILTER

56

def filter[A](fa: Iterant[A])(p: A !=> Boolean): Iterant[A] = fa match { case halt @ Halt(_) !=> halt !// !!... }

Page 57: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

FILTER

57

def filter[A](fa: Iterant[A])(p: A !=> Boolean): Iterant[A] = fa match { !// !!... case Suspend(rest, stop) !=> Suspend(() !=> filter(rest())(p), stop) !// !!... }

Page 58: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

FILTER

58

def filter[A](fa: Iterant[A])(p: A !=> Boolean): Iterant[A] = fa match { !// !!... case Next(a, rest, stop) !=> if (p(a)) Next(a, () !=> filter(rest())(p), stop) else Suspend(() !=> filter(rest())(p), stop) }

Page 59: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

TRAMPOLINES

Page 60: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

CAN WE DO THIS ?

60

case class Next[A]( item: A, rest: Future[Iterant[F, A]], stop: Future[Unit]) extends Iterant[A]

Page 61: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

type Task[+A] = () !=> Future[A]

case class Next[A]( item: A, rest: Task[Iterant[F, A]], stop: Task[Unit]) extends Iterant[A]

CAN WE DO THIS ?

61

Page 62: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

import monix.eval.Task

case class Next[A]( item: A, rest: Task[Iterant[F, A]], stop: Task[Unit]) extends Iterant[A]

CAN WE DO THIS ?

62

Page 63: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

import monix.eval.Coeval

case class Next[A]( item: A, rest: Coeval[Iterant[F, A]], stop: Coeval[Unit]) extends Iterant[A]

CAN WE DO THIS ?

63

Page 64: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

import cats.effect.IO

case class Next[A]( item: A, rest: IO[Iterant[F, A]], stop: IO[Unit]) extends Iterant[A]

CAN WE DO THIS ?

64

Page 65: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

import cats.Eval

case class Next[A]( item: A, rest: Eval[Iterant[F, A]], stop: Eval[Unit]) extends Iterant[A]

CAN WE DO THIS ?

65

Page 66: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

sealed trait Iterant[F[_], A]

case class Next[F[_], A]( item: A, rest: F[Iterant[F, A]], stop: F[Unit]) extends Iterant[F, A]

PARAMETRIC POLYMORPHISM

66

Page 67: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

PARAMETRIC POLYMORPHISM

67

import cats.syntax.all._ import cats.effect.Sync def filter[F[_], A](p: A !=> Boolean)(fa: Iterant[F, A]) (implicit F: Sync[F]): Iterant[F, A] = {

fa match { !// !!... case Suspend(rest, stop) !=> Suspend(rest.map(filter(p)), stop) !//!!... } }

Page 68: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

BRING YOUR OWN BOOZE

68

import monix.eval.Task

val sum: Task[Int] = Iterant[Task].range(0, 1000) .filter(_ % 2 !== 0) .map(_ * 2) .foldL

Page 69: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

BRING YOUR OWN BOOZE

69

import cats.effect.IO

val sum: IO[Int] = Iterant[IO].range(0, 1000) .filter(_ % 2 !== 0) .map(_ * 2) .foldL

Page 70: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

BRING YOUR OWN BOOZE

70

import monix.eval.Coeval

val sum: Coeval[Int] = Iterant[Coeval].range(0, 1000) .filter(_ % 2 !== 0) .map(_ * 2) .foldL

Page 71: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

PERFORMANCE PROBLEMS

▸Linked Lists are everywhere in FP

▸Linked Lists are terrible

▸Async or Lazy Boundaries are terrible

▸Find Ways to work with Arrays and

▸… to avoid lazy/async boundaries

71

Page 72: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

PERFORMANCE SOLUTIONS

▸Linked Lists are everywhere in FP

▸Linked Lists are terrible

▸Async or Lazy Boundaries are terrible

▸Find Ways to work with Arrays and

▸… to avoid lazy/async boundaries

72

Page 73: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

WHAT CAN ITERATE OVER ARRAYS?

73

Page 74: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

WHAT CAN ITERATE OVER ARRAYS?

74

trait Iterator[+A] { def hasNext: Boolean def next(): A }

trait Iterable[+A] { def iterator: Iterator[A] }

Page 75: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

ITERANT

WHAT CAN ITERATE OVER ARRAYS?

75

case class NextBatch[F[_], A]( batch: Iterable[A], rest: F[Iterant[F, A]], stop: F[Unit]) extends Iterant[F, A]

case class NextCursor[F[_], A]( cursor: Iterator[A], rest: F[Iterant[F, A]], stop: F[Unit]) extends Iterant[F, A]

Page 76: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and
Page 77: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE[+A]vs

ITERANT[F,A]

Page 78: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

Case Study: scanEval78

https://github.com/monix/monix/pull/412

Page 79: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

Case Study: scanEval79

import cats.effect.Sync

sealed abstract class Iterant[F[_], A] { !// !!... def scanEval[S](seed: F[S])(op: (S, A) !=> F[S]) (implicit F: Sync[F]): Iterant[F, S] = ??? }

Page 80: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

Case Study: scanEval80

def loop(state: S)(source: Iterant[F, A]): Iterant[F, S] = try source match { case Next(head, tail, stop) !=> protectedF(state, head, tail, stop) case ref @ NextCursor(cursor, rest, stop) !=> evalNextCursor(state, ref, cursor, rest, stop) case NextBatch(gen, rest, stop) !=> val cursor = gen.cursor() val ref = NextCursor(cursor, rest, stop) evalNextCursor(state, ref, cursor, rest, stop) case Suspend(rest, stop) !=> Suspend[F,S](rest.map(loop(state)), stop) case Last(item) !=> val fa = ff(state, item) Suspend(fa.map(s !=> lastS[F,S](s)), F.unit) case halt @ Halt(_) !=> halt.asInstanceOf[Iterant[F, S]] } catch { case NonFatal(ex) !=> signalError(source, ex) }

Page 81: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

Case Study: scanEval81

import cats.effect.Effect

abstract class Observable[+A] { !//… def scanEval[F[_], S](seed: F[S])(op: (S, A) !=> F[S]) (implicit F: Effect[F]): Observable[S] = ??? }

Page 82: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

Case Study: scanEval82

abstract class Observable[+A] { !//!!... def scanEval[F[_], S](seed: F[S])(op: (S, A) !=> F[S]) (implicit F: Effect[F]): Observable[S] = scanTask(Task.fromEffect(seed))((a,e) !=> Task.fromEffect(op(a,e)))

def scanTask[S](seed: Task[S])(op: (S, A) !=> Task[S]): Observable[S] = ??? }

Page 83: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

Case Study: scanEval

▸Observable’s scanTask is flatMap

▸With the aforementioned implementation

83

Page 84: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

Benchmark: scanEval84

Page 85: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

Case Study: scanEval

▸Observable has performance

▸ Iterant has reason

85

Page 86: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

Benchmark: map(f).foldL86

Page 87: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

OBSERVABLE FOLD-RIGHT▸ Cannot express foldRight for Observable

▸ But can work with substitutes, e.g. foldWhileLeftL …

87

abstract class Observable[+A] { !// !!... def foldWhileLeftL[S](seed: !=> S) (op: (S, A) !=> Either[S, S]): Task[S] }

Page 88: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

ITERANT FOLD-RIGHT

88

sealed abstract class Iterable[F[_], A] { !// !!... def foldRightL[B](b: F[B]) (f: (A, F[B], F[Unit]) !=> F[B]) (implicit F: Sync[F]): F[B] }

Page 89: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

ITERANT FOLD-RIGHT

89

def exists[F[_], A](ref: Iterant[F, A], p: A !=> Boolean) (implicit F: Sync[F]): F[Boolean] =

ref.foldRightL(F.pure(false)) { (e, next, stop) !=> if (p(e)) stop followedBy F.pure(true) else next }

Page 90: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

ITERANT FOLD-RIGHT

90

def forall[F[_], A](ref: Iterant[F, A], p: A !=> Boolean) (implicit F: Sync[F]): F[Boolean] =

ref.foldRightL(F.pure(true)) { (e, next, stop) !=> if (!p(e)) stop followedBy F.pure(false) else next }

Page 91: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

ITERANT FOLD-RIGHT

91

def concat[F[_], A](lh: Iterant[F, A], rh: Iterant[F, A]) (implicit F: Sync[F]): Iterant[F, A] =

Iterant.suspend[F, A] { lh.foldRightL(F.pure(rh)) { (a, rest, stop) !=> F.pure(Iterant.nextS(a, rest, stop)) } }

Page 92: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

ITERANT FOLD-RIGHT

92

Page 93: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

Conclusions▸Observable is best for

▸Reactive operations

▸Shared data sources

▸Throttling

▸Buffering

▸Performance

▸ Iterant is best for

▸Easier implementation reasoning

▸Converting Task / IO calls into streams

93

Page 94: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

OBSERVABLE VS ITERANT

Conclusions▸Both

▸ Implement reactive-streams.org

▸Can express asynchronous streams

▸Do back-pressuring & safe resource handling

94

Page 95: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

One more thing …

vs

Page 96: A TALE OF TWO MONIX STREAMS › public › pdfs › ScalaWorld2017-Tale-TwoStreams.pdf · MONIFU Started on January 2, 2014, at 2:30 a.m. Developed at Eloquentix for monitoring and

QUESTIONS?

monix.io

@monix@alexelcu@alexandru

alexn.org@monix


Recommended