Slide 1

Slide 1 text

࣮ફMONIXಋೖ TAISUKE OE (@OE_UIA)

Slide 2

Slide 2 text

WHO AM I ▸ Taisuke Oe ▸ ϑϦʔϥϯεΤϯδχΞ ▸ ٕज़ΞυόΠβʔ @ Septeni Original, Inc ▸ ScalaMatsuri chairperson

Slide 3

Slide 3 text

ຊ೔ͷ͓඼ॻ͖ ▸ Monixͬͯͳʹʁ ▸ Monix Taskͷ࢖͍ํ ▸ Monix Taskͷಋೖͷ࢓ํ ▸ ෇࿥: Monix 3.0.0Ͱͷมߋ఺

Slide 4

Slide 4 text

MONIXͬͯͳʹʁ MONIX ▸ ฒߦϓϩάϥϛϯάΛޮ཰Α͘ɺศརʹ͢ΔͨΊͷϥΠϒ ϥϦ ▸ ࠷৽͸v3.0.0 ▸ cats, cats-effectʹґଘ ▸ ࡞ऀ͸cats-effectͷओͨΔ։ൃऀͷҰਓ(ίϛοτ਺1Ґ) ▸ Scala Best Practices

Slide 5

Slide 5 text

MONIXͬͯͳʹʁ MONIXͷྺ࢙(͓·͚) ▸ ~v1.0 (2015/12): monifu ▸ Reactive Extensions (Rx)ʹΠϯεύΠΞ͞Εͨɺobservable sequenceʹΑΔඇಉظϓϩάϥϛϯά ϥΠϒϥϦ ▸ Zero dependency / compatible with Scalaz ▸ v1.0 monifu => monix΁໊લมߋ ▸ ‘Monifu has a lame English pronunciation’ (ӳޠͰ޷·͘͠ͳ͍ൃԻͷͨΊ) ▸ v2.0-M1 (2016/3): Taskͷ௥Ճ ▸ v2.0.0 release (2016/9) ▸ Optional packages: moni-cats / monix-scalaz-72 ▸ v3.0.0-M1 (2017/8) Iterantͷ௥Ճ ▸ deep integration with Typelevel Cats and Cats-Effect ▸ v2.3.3 release (2018/1) ▸ v3.0.0 release (2019/9) <- ΠϚίί

Slide 6

Slide 6 text

MONIXͬͯͳʹʁ MONIXͷߏ੒ ▸ monix-execution … ฒߦॲཧͰ෭࡞༻Λ͏·͘ѻ͏ͨΊͷLow Level APIɻ SchedulerͳͲɻ ▸ monix-eval … ฒߦॲཧͰ෭࡞༻Λ͏·͘ѻ͏ͨΊͷɺ७ਮؔ਺ܕσʔλߏ ଄ɻTask, CoevalͳͲɻ ▸ monix-reactive … ReactiveXͷScala࣮૷ɻObservableͳͲɻ ▸ monix-tail … PullܕετϦʔϜͷ७ਮؔ਺ܕσʔλߏ଄ɻIterantͳͲɻ ▸ monix-catnap … cats-effectͷܕΫϥεΛجʹͨ͠७ਮؔ਺ܕͷฒߦॲཧ ϢʔςΟϦςΟɻMVar, CircuitBreakerͳͲɻ

Slide 7

Slide 7 text

MONIXͬͯͳʹʁ MONIXͷߏ੒ ▸ monix-execution … ฒߦॲཧͰ෭࡞༻Λ͏·͘ѻ͏ͨΊͷLow Level APIɻSchedulerͳͲɻ ▸ monix-eval … ฒߦॲཧͰ෭࡞༻Λ͏·͘ѻ͏ͨΊͷɺ७ਮؔ਺ܕσʔλߏ ଄ɻTask, CoevalͳͲɻ ▸ monix-reactive … ReactiveXͷScala࣮૷ɻObservableͳͲɻ ▸ monix-tail … PullܕετϦʔϜͷ७ਮؔ਺ܕσʔλߏ଄ɻIterantͳͲɻ ▸ monix-catnap … cats-effectͷܕΫϥεΛجʹͨ͠७ਮؔ਺ܕͷฒߦॲཧ ϢʔςΟϦςΟɻMVar, CircuitBreakerͳͲɻ

Slide 8

Slide 8 text

TASKͷ࢖͍ํ TASK IN ACTION

Slide 9

Slide 9 text

MONIX TASKͷ࢖͍ํ BUILD.SBT ▸ libraryDependencies += "io.monix" %% "monix-eval" % “3.0.0"

Slide 10

Slide 10 text

MONIX TASKͷ࢖͍ํ MONIX TASKʹΑΔฒߦϓϩάϥϛϯάୈҰา import Scheduler.Implicits.global def task(i: Int) = Task.eval { printCurrentThread() i } val cubeSum = for { i <- task(11) //on run-main-0 iCube <- Task.evalAsync(i * i * i) j <- task(13) //on scala-execution-context-global-57 jCube <- Task.evalAsync(j * j * j) } yield { printCurrentThread() //on scala-execution-context-global-57 iCube + jCube } cubeSum.runAsync { case Right(value) => println(value) case Left(err) => println(err.getMessage) }

Slide 11

Slide 11 text

MONIX TASKͷධՁઓུ MONIX TASK͸஗ԆධՁ ▸ ஗ԆධՁTaskΠϯελϯεΛੜ੒ͨ࣌͠఺Ͱ ͸ɺ࣮ߦ͞Εͳ͍ͷͰࢀরಁաɻ ▸ ஗ԆධՁ͔ͭಉظ ▸ Task.apply ▸ v3.0.0͔Βಉظʹͳͬͨ ▸ Task.eval ▸ Task.evalOnce ▸ ϝϞԽ ▸ ஗ԆධՁ͔ͭඇಉظ ▸ Task.evalAsync ▸ ઌߦධՁ͔ͭಉظ ▸ Task.now ▸ Task.raiseError ▸ ܭࢉࡁΈͷ஋Λϥοϓ͢Δ

Slide 12

Slide 12 text

MONIX TASKͷධՁઓུ ஗ԆධՁTASKΛ࣮ߦ͢Δ ▸ ࣮ߦ͢Δࡍʹ͸͡ΊͯɺScheduler (ExecutionContextΛϥοϓͨ͠΋ͷʣΛ҉໧ͷҾ਺Ͱ ཁٻ͢Δ ▸ ࣮ߦ͢Δͱɺ෭࡞༻͕ൃੜ͢ΔʢՄೳੑ͕͋Δʣ ▸ Task#runAsync ▸ Task#runAsync(cb: (Either[Throwable, A]) => Unit)(implicit s: Scheduler): Cancelable ▸ Task#runAsyncUncancelable ▸ Task#runAsyncUncancelable(cb: (Either[Throwable, A]) => Unit)(implicit s: Scheduler): Unit ▸ Task#runAsyncAndForget ▸ Task#runAsyncAndForget(implicit s: Scheduler): Unit ▸ Task#runToFuture ▸ Task#runToFuture(implicit s: Scheduler): CancelableFuture[A]

Slide 13

Slide 13 text

MONIX TASKͷ࢖͍ํ MONIX TASKʹ͓͚ΔඇಉظڥքΛཧղ͠Α͏ import Scheduler.Implicits.global def task(i: Int) = Task.eval { printCurrentThread() i } val cubeSum = for { i <- task(11) //on run-main-0 iCube <- Task.evalAsync(i * i * i) j <- task(13) //on scala-execution-context-global-57 jCube <- Task.evalAsync(j * j * j) } yield { printCurrentThread() //on scala-execution-context-global-57 iCube + jCube } cubeSum.runAsync{ case Right(value) => println(value) case Left(err) => println(err.getMessage) }

Slide 14

Slide 14 text

MONIX TASKͷ࢖͍ํ MONIX TASKʹ͓͚ΔඇಉظڥքΛཧղ͠Α͏ import Scheduler.Implicits.global def task(i: Int) = Task.eval { printCurrentThread() i } val cubeSum = for { i <- task(11) //on run-main-0 iCube <- Task.evalAsync(i * i * i) j <- task(13) //on scala-execution-context-global-57 jCube <- Task.evalAsync(j * j * j) } yield { printCurrentThread() //on scala-execution-context-global-57 iCube + jCube } cubeSum.runAsync{ case Right(value) => println(value) case Left(err) => println(err.getMessage) } ඇಉظڥքʁ

Slide 15

Slide 15 text

ඇಉظڥք ඇಉظڥքͱ͸ʁ ▸ ʮReactive Manifest GlossaryʯΑΓ ▸ Isolation can be defined in terms of decoupling …… Decoupling in time means that the sender and receiver …… do not need to be present at the same time for communication to be possible. It is enabled by adding asynchronous boundaries between the components ……

Slide 16

Slide 16 text

ඇಉظڥք ඇಉظڥքͱ͸ʁ ▸ ʮReactive Manifest GlossaryʯΑΓ ▸ ִ཭ ͸ɺ੾Γ཭͢͜ͱʹΑͬͯୡ੒͞ΕΔ……࣌ؒʹ ͓͚Δ੾Γ཭͠ͱ͸ɺૹΓखͱड͚ख͕…….ಉ࣌ʹίϛϡχέʔ γϣϯ͢Δඞཁ͕ͳ͍ͱ͍͏͜ͱΛҙຯ͢Δɻ͜ͷ੾Γ཭͠͸ɺ ίϯϙʔωϯτؒʹඇಉظڥքΛૠೖ͢Δ͜ͱͰՄೳʹͳΔ…… ▸ ͢ͳΘͪඇಉظڥք͸ɺૹΓखͱड͚ख͕ʮಉظ௨৴͢Δඞཁ͕ͳ ͍ʯͱ͍͏ؔ܎Λ໌ࣔ͢ΔͨΊͷ΋ͷ

Slide 17

Slide 17 text

MONIX TASKͷඇಉظڥք TASK ʹ͓͚Δඇಉظڥք ▸ (task: Task).executeAsync ▸ taskͷલʹඇಉظڥքΛૠೖ ▸ Task.evalAsync(…) 㱻 Task.eval(…).executeAsync ▸ (task: Task).asyncBoundary ▸ taskͷޙʹඇಉظڥքΛૠೖ

Slide 18

Slide 18 text

MONIX TASKͷඇಉظڥք ඇಉظڥք͸Ͳ͜ʁ import Scheduler.Implicits.global def task(i: Int) = Task.eval { printCurrentThread() i } val cubeSum = for { i <- task(11) //on run-main-0 iCube <- Task.evalAsync(i * i * i) j <- task(13) //on scala-execution-context-global-57 jCube <- Task.evalAsync(j * j * j) } yield { printCurrentThread() //on scala-execution-context-global-57 iCube + jCube } cubeSum.runAsync{ case Right(value) => println(value) case Left(err) => println(err.getMessage) }

Slide 19

Slide 19 text

MONIX TASKͷඇಉظڥք ඇಉظڥք͸͜͜ import Scheduler.Implicits.global def task(i: Int) = Task.eval { printCurrentThread() i } val cubeSum = for { i <- task(11) //on run-main-0 iCube <- Task.evalAsync(i * i * i) j <- task(13) //on scala-execution-context-global-57 jCube <- Task.evalAsync(j * j * j) } yield { printCurrentThread() //on scala-execution-context-global-57 iCube + jCube } cubeSum.runAsync{ case Right(value) => println(value) case Left(err) => println(err.getMessage) } ඇಉظڥք ඇಉظڥք

Slide 20

Slide 20 text

MONIX TASKͷඇಉظڥք εϨου͕੾ΓସΘͬͯΔͷ͸͜͜ import Scheduler.Implicits.global def task(i: Int) = Task.eval { printCurrentThread() i } val cubeSum = for { i <- task(11) //on run-main-0 iCube <- Task.evalAsync(i * i * i) j <- task(13) //on scala-execution-context-global-57 jCube <- Task.evalAsync(j * j * j) } yield { printCurrentThread() //on scala-execution-context-global-57 iCube + jCube } cubeSum.runAsync{ case Right(value) => println(value) case Left(err) => println(err.getMessage) } ඇಉظڥք ඇಉظڥք ݺͼग़͠εϨουʢϝΠϯʣ ExecutionContext಺ͷεϨουϓʔϧ ExecutionContext಺ͷεϨουϓʔϧ

Slide 21

Slide 21 text

εϨου͕੾ΓସΘͬͯΔͷ͸͜͜ import Scheduler.Implicits.global def task(i: Int) = Task.eval { printCurrentThread() i } val cubeSum = for { i <- task(11) //on run-main-0 iCube <- Task.evalAsync(i * i * i) j <- task(13) //on scala-execution-context-global-57 jCube <- Task.evalAsync(j * j * j) } yield { printCurrentThread() //on scala-execution-context-global-57 iCube + jCube } cubeSum.runAsync{ case Right(value) => println(value) case Left(err) => println(err.getMessage) } ඇಉظڥք ඇಉظڥքʁ ݺͼग़͠εϨουʢϝΠϯʣ ExecutionContext಺ͷεϨουϓʔϧ MONIX TASKͷඇಉظڥք

Slide 22

Slide 22 text

MONIX TASKͷඇಉظڥք MONIX TASKͷඇಉظڥքͷऔΓѻ͍ ▸ ඇಉظڥքΛૠೖͨ͠ͱ͜ΖͰ ▸ ಉ࣌ʹSchedulerΛ౉͍ͯ͠Δ৔߹͸ɺͦͷSchedulerͷ΋ͭεϨουϓʔϧͰ࣮ߦ͞Ε Δɻ ▸ Scheduler͕ࢦఆ͞Εͳ͍৔߹͸ɺrunAsync౳Ͱ҉໧తʹ౉ͨ͠SchedulerʢσϑΥϧτ ͷSchedulerʣ಺ͷεϨουϓʔϧ΁੾ΓସΘΔ ▸ طʹσϑΥϧτͷScheduler಺ͷεϨουϓʔϧͰ࣮ߦதͷ৔߹͸ɺ࣮ߦεϨου͸ ੾ΓସΘΒͳ͍͜ͱ͕͋Δ ▸ Scalaඪ४Future͸ɺmap / flatMap͝ͱʹڧ੍తʹεϨουϓʔϧʹ౉͞ΕΔ ▸ Φʔόʔϔου͕େ͖͍

Slide 23

Slide 23 text

TASK ʹ͓͚ΔඇಉظڥքʴSCHEDULERΛࢦఆ ▸ (task: Task).asyncBoundary(s: Scheduler) ▸ taskͷޙʹඇಉظڥքΛૠೖ͠ɺ͔࣮ͭߦ͢ΔSchedulerΛࢦఆ͢Δɻ ▸ (task: Task).executeOn(s: Scheduler) ▸ taskͷલʹඇಉظڥքΛૠೖ͠ɺ͔࣮ͭߦ͢ΔSchedulerΛࢦఆ͢Δɻ ▸ taskͷޙʹ΋ඇಉظڥքΛૠೖ͠ɺ͔࣮ͭߦ͢ΔSchedulerΛσϑΥϧ τ΁੾Γସ͑Δ ▸ v3.0.0͔ΒɺSwitch Back͢ΔΑ͏ʹͳͬͨ MONIX TASKͷඇಉظڥք

Slide 24

Slide 24 text

࣮ߦ͢ΔSCHEDULERΛࢦఆ͢Δ import monix.eval.Task def task(i: Int) = Task.eval { printCurrentThread() i } def heavyCalculation(i: Int): Task[BigDecimal] = Task.eval { printCurrentThread() Seq.fill(100)(i).foldLeft(BigDecimal(i))(_ * _) } import Scheduler.Implicits.global val io = Scheduler.io("blocking") val result = for { value <- task(151) // on run-main-24 another <- task(100) // on run-main-24 calculated <- heavyCalculation(value).executeOn(io) // on blocking-808 calcu <- heavyCalculation(another).executeOn(io) // on blocking-808 } yield calculated + calcu result.runAsync { case Right(value) => printCurrentThread() // on scala-execution-context-global-809 println(value) case Left(err) => println(err) } MONIX TASKͷඇಉظڥք

Slide 25

Slide 25 text

MONIX TASKͷ࢖͍ํ SCALAඪ४FUTUREͱͷINTEROP ▸ Scalaඪ४ Futureͱͷ૬ޓม׵͕ޮ཰Α͘؆୯ʹͰ͖Δ ▸ Task.fromFuture[A](f: Future[A]): Task[A] ▸ Task.deferFuture[A](fa: => Future[A]): Task[A] ▸ Task.deferFutureAction[A](f: (Scheduler) => Future[A]): Task[A] ▸ Task#runToFuture(implicit s: Scheduler): CancelableFuture[A]

Slide 26

Slide 26 text

SCALAඪ४FUTUREͱͷINTEROP TASK . FROM FUTURE ▸ Task.fromFuture[A](f: Future[A]): Task[A] ▸ ઌߦධՁ(eager)͞ΕΔTaskΛ࡞Δ ▸ Future.successful΍Future.faildͰఆٛ͞ΕͨFutureͳ ͲɺܭࢉࡁΈͷFutureΛแΉɻ

Slide 27

Slide 27 text

SCALAඪ४FUTUREͱͷINTEROP TASK . DEFER FUTURE ▸ Task.deferFuture[A](fa: => Future[A]): Task[A] ▸ ஗ԆධՁ(lazy)͞ΕΔTaskΛ࡞Δ ▸ (ExecutionContextΛཁٻ͠ͳ͍) FutureΛฦ͢ܭࢉΛแ Ήɻ

Slide 28

Slide 28 text

SCALAඪ४FUTUREͱͷINTEROP TASK . DEFER FUTURE ACTION ▸ Task.deferFutureAction[A](f: (Scheduler) => Future[A]): Task[A] ▸ ஗ԆධՁ(lazy)͞ΕΔTaskΛ࡞Δɻ ▸ Scheduler (ExecutionContext)ΛҾ਺ʹͱΔɺFutureΛฦ ͢ܭࢉΛแΉɻ͜ͷFutureʹ౉͞ΕΔExecutionContext ͸ɺ࠷ޙʹTask#runAsyncͰ౉ͨ͠SchedulerͷΠϯελ ϯεͰ͋Δɻ

Slide 29

Slide 29 text

SCALAඪ४FUTUREͱͷINTEROP TASK . RUN TO FUTURE ▸ Task#runToFuture(implicit s: Scheduler): CancelableFuture[A] ▸ TaskͷධՁΛ։࢝͠ɺFuture΁ม׵͢Δ

Slide 30

Slide 30 text

MONIX TASKͷ࢖͍ํ ਖ਼ৗύεҎ֎ͷϋϯυϦϯά ▸ ΤϥʔϋϯυϦϯά & ϦτϥΠ ▸ Ωϟϯηϧ ▸ Ϧιʔεղ์

Slide 31

Slide 31 text

MONIX TASKͷ࢖͍ํ ΤϥʔϋϯυϦϯά import monix.eval.Task //ࣦഊ͢Δ͔΋ def loadHeavyJson(id: Long): Task[JSON] = ??? import scala.concurrent.duration._ // Exponential BackoffͰ࠶ࢼߦ // 100 millis, 200 millis, 400 millis, 800 millis, 1600 millis, 3200 millis val heavyJson: Task[JSON] = ɹloadHeavyJson(123L).onErrorRestartLoop(100.millis) { (e, delay, retry) => if(delay < 4.seconds) retry(delay * 2).delayExecution(delay) else Task.raiseError(e) ɹ}.memoizeOnSuccess //੒ޭͨ͠ΒϝϞԽ

Slide 32

Slide 32 text

MONIX TASKͷ࢖͍ํ Ωϟϯηϧ import monix.eval.Task import monix.execution.Scheduler.Implicits.global val cancelable = heavyJson.runAsync { case Left(e) => println(e) case Right(json) => println(json) } //΋͠ඞཁͳΒ cancelable.cancel()

Slide 33

Slide 33 text

MONIX TASKͷ࢖͍ํ Ϧιʔεղ์ import monix.eval.Task class Client() { def get(id: Long): JSON = ??? def release(): Unit = ??? } def loadHeavyJsonByClient(id: Long): Task[JSON] = Task.eval(new Client()) .bracket { client => Task.evalAsync(client.get(id)) } { client => //੒൱ʹؔΘΒͣϦϦʔε Task.eval(client.release()) }

Slide 34

Slide 34 text

ฒྻܭࢉAPI ▸ tasks: Seq[Task[T]]͕͋ͬͨͱ͖ ▸ Task.sequence(tasks) Ͱ௚ྻܭࢉ͢ΔTask[Seq[T]] ▸ Task.gather(tasks)Ͱฒྻܭࢉ͢ΔTask[Seq[T]] ▸ Task.gatherN(parallerism:Int)(tasks)Ͱɺಉ࣌ฒྻ਺ࢦఆͰ ฒྻܭࢉ͢ΔTask[List[A]] ▸ Task.parMap2(taskA, taskB)((A,B) => R)Ͱɺฒྻܭࢉ͠Task[R] MONIX TASKͷ࢖͍ํ

Slide 35

Slide 35 text

MONIX TASKͷ࢖͍ํ ؔ਺ܕ༝དྷͷܕΫϥεؔ܎ͷ஌͕ࣝෆཁ ▸ MonadError, Applicative, Traverseͱ͍ͬͨɺ௨ৗؔ਺ܕϓ ϩάϥϛϯά༻ͷϥΠϒϥϦ(e.g. Cats౳)ͷܕΫϥε͕ఏڙ ͍ͯ͠Δsyntaxͷ͏ͪɺಛʹศརͳ΋ͷ͕MonixࣗલͰ༻ҙ ͞Ε͍ͯΔ ▸ Cats΍Scalazʹਫ਼௨͍ͯ͠ͳͯ͘΋ɺTaskΛ࢖͍͜ͳ͢͜ ͱ͕Ͱ͖Δɻ

Slide 36

Slide 36 text

ؔ਺ܕ ܕΫϥεෆཁͷTASK API TASK.ATTEMPT ▸ Task#attempt: Task[Either[Throwable, A]] ▸ MonadErrorܕΫϥεͷattemptͱಉ౳ ▸ ੒ޭ஋΋͘͠͸ࣦഊ஋ΛEitherܕ΁্࣋ͪ͛Δ ▸ ੒ޭ͍ͯ͠Δ৔߹͸Task(Right(_: A)) ▸ ࣦഊ͍ͯ͠Δ৔߹͸Task(Left(_: Throwable))

Slide 37

Slide 37 text

ؔ਺ܕ ܕΫϥεෆཁͷTASK API TASK.MAP2 / TASK.PARMAP2 OR MORE ▸ Task.map2[A1, A2, R](fa1: Task[A1], fa2: Task[A2])(f: (A1, A2) => R): Task[R] ͳͲͷmapN, parMapN ▸ ApplicativeܕΫϥεͷmapNͱಉ౳(࣮૷ґଘͰ௚ྻorฒྻܭࢉ ͞ΕΔ) ▸ map2͸fa1ͱfa2͸௚ྻʹܭࢉ͞ΕΔ ▸ fa1ͷ׬ྃޙʹfa2͕ܭࢉ͞ΕΔ ▸ parMap2͸fa1ͱfa2͸ฒྻʹܭࢉ͞ΕΔ

Slide 38

Slide 38 text

ؔ਺ܕ ܕΫϥεෆཁͷTASK API TASK.SEQUENCE ▸ Task.sequence[A, M[X] <: Iterable[X]](in: M[Task[A]]) (implicit bf: BuildFrom[M[Task[A]], A, M[A]]): Task[M[A]] ΍ɺTask.traverse ▸ TraverseܕΫϥεͷsequence , traverseͱ΄΅ಉ౳ ▸ MͱTaskͷೖΕࢠͷॱ൪ΛೖΕସ͑Δɻ

Slide 39

Slide 39 text

ؔ਺ܕ ܕΫϥεෆཁͷTASK API TASKͱܕΫϥε ▸ Task͸஗ԆධՁ͢Δࢀরಁաͳσʔλߏ଄ɻlawfulͳܕΫϥεΠϯελϯε΋༻ҙ͞Ε͍ͯΔɻ ▸ ७ਮؔ਺ܕͷઃܭΛͨ͠ιϑτ΢ΣΞͰ΋࢖͍΍͍͢ɻ ▸ cats-effect: ▸ Effect, ConcurrentEffect, Async, Concurrent, ContextShift ▸ cats: ▸ MonadError, Applicative, Monad, CoFlatMap ▸ Parallel, Monoid, Semigroup ▸ Future͸ઌߦධՁͰ෭࡞༻ΛҾ͖ىͨ͜͢ΊɺMonad౳ͷܕΫϥεͷlawΛຬͨ͞ͳ͍ɻ MonadΛཁٻ͍ͯ͠Δͱ͜ΖͰFutureΛ࢖͏ͱɺ༧ظͤ͵ΤϥʔΛҾ͖ى͜͢Մೳੑ͋Γɻ

Slide 40

Slide 40 text

MONIX TASKͷ࢖͍ํ MONIX TASK ͷخ͍͠ͱ͜Ζʢ·ͱΊʣ ▸ ॊೈʹඇಉظڥքΛઃఆՄೳ ▸ ඞཁͳ৔ॴͰඞཁͳ͚ͩɺඇಉظॲཧʹ͢Δ͜ͱ͕Մೳ ▸ Scala Futureͱͷinterop͕༏ल ▸ Lightbend Ecosystemʹ৐ͬͨΞϓϦέʔγϣϯͰ΋ಋೖ͠΍͍͢ ▸ ਖ਼ৗύεҎ֎ͷϋϯυϦϯά͕ॊೈ ▸ ΤϥʔɺϦτϥΠɺΩϟϯηϧɺϦιʔεղ์ͳͲ ▸ ฒྻܭࢉAPI͕๛෋ ▸ ஗ԆධՁͷϝϦοτΛڗड ▸ ؔ਺ܕ༝དྷͷܕΫϥεͷ஌͕ࣝͳͯ͘΋े෼࢖͑Δ

Slide 41

Slide 41 text

TASKͷಋೖͷ࢓ํ TASK IN ACTION

Slide 42

Slide 42 text

MONIX TASKͷಋೖ TASKΛͲ͔͜Βಋೖͨ͠ΒΑ͍͔ʁ ▸ ඇಉظॲཧʹFutureΛ࢖࣮ͬͯ૷͞ΕͨγεςϜͰɺTaskΛ ࢖͍͍ͨ৔ॴ͕Ͱ͖ͨέʔε ▸ retry΍timeoutɺcancelͳͲࣦഊύεͷ؅ཧΛॊೈʹͨ͠ ͍ ▸ ϝϞԽΛॊೈʹߦ͍͍ͨ ▸ FutureͰඇಉظڥքΛ·͙ͨΦʔόʔϔου(map/ flatMapຖ)ΛݮΒ͍ͨ͠

Slide 43

Slide 43 text

TASKΛͲ͔͜Βಋೖͨ͠ΒΑ͍͔ʁ Domain Service Repository Repository Impl Application Service Controller Future Future Future MONIX TASKͷಋೖ

Slide 44

Slide 44 text

FUTUREͰ࣮૷͞ΕͨγεςϜ D0MAIN trait UserRepository { type Ctx <: Context def resolveById(id: UserId)(implicit C: Ctx): Future[User] def update(user: User)(implicit C: Ctx): Future[Unit] } trait UserNameChangeService { val repository: UserRepository def changeName(userId: UserId, newName: String)(implicit C: repository.Ctx): Future[Unit] = { implicit val ec = C.ec for { user <- repository.resolveById(userId) modifiedUser = user.copy(name = newName) _ <- repository.update(modifiedUser) } yield () } } trait Context { def ec: ExecutionContext } case class UserId(value: Long) extends AnyVal case class User(id: UserId, name: String)

Slide 45

Slide 45 text

APPLICATION class UserRepositoryImpl extends UserRepository { override type Ctx = FutureIOContext import UserRecord.{u, userIdBinder} override def resolveById(id: UserId)(implicit C: FutureIOContext): Future[User] = { import C._ Future( Future.fromTry( withSQL { select.from(UserRecord as u).where.eq(u.id, id).limit(1) }.map(UserRecord(_, u.resultName)).single().apply() .fold[Try[User]](Failure( /* … */ )))(ur => Success(ur.toUser)) ) ).flatten } override def update(user: User)(implicit C: FutureIOContext): Future[Unit] = { import C._ Future { withSQL { QueryDSL.update(UserRecord as u).set(u.id -> user.id.value, u.name -> user.name) }.update().apply() } } } class UserNameChangeServiceImpl(override val repository: UserRepositoryImpl) extends UserNameChangeService { def changeNameTx(userId: UserId, newName: String)(implicit ec: ExecutionContext): Future[Unit] = DB.localTx { /* … * / } } case class FutureIOContext(session: DBSession, ec: ExecutionContext) extends Context{ implicit val dbSession: DBSession = session implicit val executionContext: ExecutionContext = ec } FUTUREͰ࣮૷͞ΕͨγεςϜ

Slide 46

Slide 46 text

CONTROLLER object Runner extends App { val repository = new UserRepositoryImpl() val service = new UserNameChangeServiceImpl(repository) import ExecutionContext.Implicits.global service.changeNameTx(UserId(1L), "Martin Odersky").onComplete { case Success(_) => //do something case Failure(_) => //do something } } FUTUREͰ࣮૷͞ΕͨγεςϜ

Slide 47

Slide 47 text

TASKΛͲ͔͜Βಋೖͨ͠ΒΑ͍͔ʁ Domain Service Repository Repository Impl Application Service Controller Future Future Task Future MONIX TASKͷಋೖ

Slide 48

Slide 48 text

Domain Service Repository Repository Impl Application Service Controller Future Future Task Task.deferFutureAction Future TASKΛͲ͔͜Βಋೖͨ͠ΒΑ͍͔ʁ MONIX TASKͷಋೖ

Slide 49

Slide 49 text

CONTROLLERͰTASKΛ࢖͏ object Runner extends App { val repository = new UserRepositoryImpl() val service = new UserNameChangeServiceImpl(repository) val task = Task.deferFutureAction { implicit scheduler => service.changeNameTx(UserId(1L), "Martin Odersky") }.onErrorRestartLoop(0) { (t, current, handle) => if (current < 5) handle(current + 1).delayExecution(300.millis) else Task.raiseError(t) } import monix.execution.Scheduler.Implicits.global task.runAsync { case Left(_) => //do something case Right(_) => //do something } } FUTUREͰ࣮૷͞ΕͨγεςϜ

Slide 50

Slide 50 text

΋͠APPLICATION SERVICEͰTASKΛ࢖͍ͨ͘ͳͬͨΒ Domain Service Repository Repository Impl Application Service Controller Future Future Task Task.deferFutureAction Future MONIX TASKͷಋೖ

Slide 51

Slide 51 text

΋͠APPLICATION SERVICEͰTASKΛ࢖͍ͨ͘ͳͬͨΒ Domain Service Repository Repository Impl Application Service Controller Future Future Task Task.deferFutureAction Future Task Task.deferFutureAction MONIX TASKͷಋೖ

Slide 52

Slide 52 text

΋͠APPLICATION SERVICEͰTASKΛ࢖͍ͨ͘ͳͬͨΒ Domain Service Repository Repository Impl Application Service Controller Task Future Future Task Task.deferFutureAction MONIX TASKͷಋೖ

Slide 53

Slide 53 text

APPLICATION SERVICEͰTASKΛ࢖͏ class UserNameChangeServiceImpl(override val repository: UserRepositoryImpl) extends UserNameChangeService { def changeNameTx(userId: UserId, newName: String): Task[Unit] = DB.localTx { session => Task.deferFutureAction{ scheduler => implicit val ctx = FutureIOContext(session, scheduler) changeName(userId, newName) } } } FUTUREͰ࣮૷͞ΕͨγεςϜ

Slide 54

Slide 54 text

΋͠REPOSITORYͷ࣮૷ͰTASKΛ࢖͍ͨ͘ͳͬͨΒ Domain Service Repository Repository Impl Application Service Controller Task Future Future Task Task.deferFutureAction MONIX TASKͷಋೖ

Slide 55

Slide 55 text

΋͠REPOSITORYͷ࣮૷ͰTASKΛ࢖͍ͨ͘ͳͬͨΒ Domain Service Repository Repository Impl Application Service Controller Task Future Future Task Task.deferFutureAction Task Task.deferFutureAction MONIX TASKͷಋೖ

Slide 56

Slide 56 text

MONIX TASKͷಋೖͷ࢓ํ - ·ͱΊ ▸ Task͸ॊೈͳඇಉظڥքͷઃఆɺࣦഊύεͷϋϯυϦϯάͳͲɺ࣮༻ తͳAPI͕ἧ͍ͬͯΔ ▸ ControllerͳͲɺෳࡶͳඇಉظॲཧ΍ɺࣦഊͷϋϯυϦϯά͕ٻΊΒ ΕΔͱ͜ΖͰಋೖ͠΍͍͢ ▸ Task͸Futureͱͷinterop͕ॆ࣮͍ͯ͠ΔͨΊɺincrementalʹಋೖ͢Δ ͜ͱ͕Ͱ͖Δ ▸ ಉظॲཧͷͨΊʹFuture / Try͕ࠞࡏ͍ͯ͠Δ৔߹ɺඇಉظTaskͱಉ ظTaskʹͦΕͧΕஔ׵Մೳ MONIX TASKͷಋೖ

Slide 57

Slide 57 text

MONIXΛ࢖͏ͱ͖ -༨ஊ ▸ Effectܕ Injection (a.k.a “tagless final”) ͢Δ΂͖͔ ▸ Monix TaskΛ࢖͏্Ͱ͸ඞਢͰ͸ͳ͍ɻReaderTͱ߹Θͤ ͯ࢖͍͍ͨ࣌ͳͲɺศརͳ࣌΋͋Δɻ ▸ ςελϏϦςΟͳͲɺଞͷ࣠Ͱಋೖ͢΂͖͔ݕ౼͠·͠ΐ ͏ɻͦͷ্Ͱ͸ϝϦοτ͕͋ΔͳΒಋೖ͢Ε͹ྑ͍ɻ MONIX TASKͷಋೖ

Slide 58

Slide 58 text

APPENDIX MONIX 3.0.0ͷओͳมߋ఺ ▸ ґଘϥΠϒϥϦͷมߋ ▸ APIͷमਖ਼ ▸ ڍಈͷมߋ ▸ ໊લͷमਖ਼ ▸ ฒྻܭࢉAPIͷ֦ॆ ▸ Iterant, Fiber, Localͷ௥Ճ

Slide 59

Slide 59 text

MONIX 3.0.0ͷมߋ఺ ґଘϥΠϒϥϦͷมߋ ▸ Monix v2.3.3Ͱ͸ɺcore͸ґଘͤͣɺScalazͱCatsͦΕͧΕ ʹґଘͨ͠module͕͋ͬͨ ▸ Monix v3.0.0͔ΒҎԼͷϥΠϒϥϦʹґଘ͍ͯ͠Δ ▸ Cats 2.0.0 ▸ Cats-effect 2.0.0

Slide 60

Slide 60 text

MONIX 3.0.0ͷมߋ఺ APIͷमਖ਼ - ڍಈͷมߋ ▸ Task.apply͕ඇಉظ͔Βಉظ΁ ▸ https://github.com/monix/monix/pull/670 ▸ https://gitter.im/monix/monix/archives/2018/05/10? at=5af3f835bd10f34a68ef1f1f

Slide 61

Slide 61 text

MONIX 3.0.0ͷมߋ఺ APIͷमਖ਼ - ڍಈͷมߋ ▸ Task#executeOn͕ɺσϑΥϧτscheduler΁ࣗಈͰswitch back ▸ https://github.com/monix/monix/pull/670 ▸ https://gitter.im/monix/monix/archives/2018/05/10? at=5af3fffab84be71db9f6bfdb ▸ https://gitter.im/monix/monix/archives/2018/05/10? at=5af3fe575a1d895fae2bb6fc

Slide 62

Slide 62 text

MONIX 3.0.0ͷมߋ఺ APIͷमਖ਼ - ڍಈͷมߋ ▸ Task.mapBothͷҾ਺͕ಉظTaskͰ͋ͬͨͱͯ͠΋ɺࣗಈͰ ඇಉظʹฒྻܭࢉ͞ΕΔΑ͏मਖ਼͞Εͨ

Slide 63

Slide 63 text

MONIX 3.0.0ͷมߋ఺ APIͷमਖ਼ - ໊લͷมߋ ▸ Task.zipMapNܥ => Task.mapNܥ ▸ Task#executeWithFork => Task#executeAsync

Slide 64

Slide 64 text

APIͷमਖ਼ - RUNܥAPIͷ੔ཧ ▸ Task#runAsync => Task#runToFuture ▸ Task#runOnComplete(Try[A] => Unit) => Task#runAsync(Either[Throwable, A] => Unit) MONIX 3.0.0ͷมߋ఺

Slide 65

Slide 65 text

MONIX 3.0.0ͷมߋ఺ APIͷ௥Ճ - RUNܥAPIͷ௥Ճ ▸ Task#runSyncStepܥ ▸ Task#runAsyncAndForgetܥ ▸ Task#runAsyncOptͳͲOptionܥ

Slide 66

Slide 66 text

MONIX 3.0.0ͷมߋ఺ APIͷ௥Ճ - ௚ྻɾฒྻܭࢉAPIͷ֦ॆ ▸ Task.gatherN, Task.parMapܥ, Task.wanderN͕௥Ճ͞Εͨ

Slide 67

Slide 67 text

MONIX 3.0.0ͷมߋ఺ APIͷ௥Ճ - ϦιʔεˍΤϥʔϋϯυϦϯάܥ ▸ Task#bracketܥ ▸ Task#guaranteeܥ ▸ Task#redeemܥ ▸ Task#runAsyncFܥ

Slide 68

Slide 68 text

MONIX 3.0.0ͷมߋ఺ APIͷ௥Ճ - ϧʔϓܥϝιου௥Ճ ▸ Task#loopForever ▸ Task#onErrorRestartLoop

Slide 69

Slide 69 text

MONIX 3.0.0ͷมߋ఺ APIͷ௥Ճ - CATS-EFFECT COMPATIBLEͳܕม׵ ▸ Task#toܥ ▸ to[F[_]], toAsync[F[_]], toConcurrent[F[_]] ▸ Task.fromܥ ▸ fromEffect, fromConcurrentEffectͳͲ

Slide 70

Slide 70 text

MONIX 3.0.0ͷมߋ఺ MONIX 3.0.0 ͷ஫ҙࣄ߲ ▸ 2019/10/27࣌఺Ͱ͸υΩϡϝϯτͷߋ৽͕௥͍͍͍ͭͯͳ͍ ▸ Documentation, scaladocͷίϝϯτ ▸ େےͰ͸มߋ͸ͳ͍͕ɺࡉ෦ͷهड़͸৴༻͠ͳ͍ํ͕͍͍ ▸ ඞͣ3.0.0Ͱಈ࡞֬ೝ͠Α͏ ▸ GitHub issues / PullRequests΋ඞཁʹԠͯ͡ࢀর͠Α͏ ▸ PullReqνϟϯε!!