Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Scale By the Bay 2017 Index your State

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

Scale By the Bay 2017 Index your State

Avatar for Vincent Marquez

Vincent Marquez

November 22, 2017

More Decks by Vincent Marquez

Other Decks in Programming

Transcript

  1. trait Conn { def setDest(u: URI): Unit def initCredentials(s: String):

    Unit def connect(): Unit def sendData(a: Array[Byte]): Unit }
  2. 2nd Try trait Conn { def setDest(str: URI): Conn def

    initCredentials(s: String): Future[Conn] def connect(): Future[Conn] def sendData(a: Array[Byte]): Future[Conn] }
  3. Using the API is kind of weird…
 
 for {

    c1 <- conn.initCredentials("asdf") c2 <- c1.connect() c3 <- c2.sendData(arr) } yield ()
  4. Using the API is kind of weird…
 
 for {

    c1 <- conn.initCredentiasl("asdf") c2 <- c1.connect() c3 <- c2.sendData(arr) } yield ()
  5. Using the API is kind of weird…
 
 for {

    c1 <- conn.initCredentiasl("asdf") c2 <- c1.connect() c3 <- c2.sendData(arr) } yield ()
  6. What is a State Monad? LYAHFGG: We’ll say that a

    stateful computation is a function that takes some state and returns a value along with some new state. That function would have the following type: A => (A, B)

  7. State example: Counter def countState = SState[Int, String]((i: Int) =>

    (i+1, i.toString)) for { a <- countState b <- countState } yield (a + b).toUpperCase
  8. Simple State case class SState[A, B](f: (A => (A,B))) {

    def flatMap[C](fc: B => SState[A, C]): SState[A, C] = SState((a: A) => f(a) match { case (a, b) => fc(b).f(a) }) }
  9. Simple State flatMap case class SState[A, B](f: (A => (A,B)))

    { def flatMap[C](fc: B => SState[A, C]): SState[A, C] = SState((a: A) => f(a) match { case (a, b) => fc(b).f(a) }) }
  10. Simple State flatMap case class SState[A, B](f: (A => (A,B)))

    { def flatMap[C](fc: B => SState[A, C]): SState[A, C] = SState((a: A) => f(a) match { case (a, b) => fc(b).f(a) }) }
  11. Simple State flatMap case class SState[A, B](f: (A => (A,B)))

    { def flatMap[C](fc: B => SState[A, C]): SState[A, C] = SState((a: A) => f(a) match { case (a, b) => fc(b).f(a) }) }
  12. Simple State flatMap case class SState[A, B](f: (A => (A,B)))

    { def flatMap[C](fc: B => SState[A, C]): SState[A, C] = SState((a: A) => f(a) match { case (a, b) => fc(b).f(a) }) }
  13. Cool let’s check out the Scalaz State Monad… found :

    scalaz.State[Int,String] (which expands to) scalaz.IndexedStateT[scalaz.Id.Id,Int,Int,String]
  14. Let’s look at the source:
 sealed abstract class IndexedStateT[F[_], -S1,

    S2, A] { self => def getF[S <: S1]: Monad[F] => F[S => F[(S2, A)]] /** Run and return the final value and state in the context of `F` */ def apply(initial: S1)(implicit F: Monad[F]): F[(S2, A)] = F.join(F.map[S1 => F[(S2, A)], F[(S2, A)]](getF(F))(sf => sf(initial))) …
  15. StateT time Our tuple is inside the context case class

    SStateT[F[_], A, B](f: (A => F[(A, B)])) { def flatMap[C](fc: B => SStateT[F, A, C]) (implicit M: Monad[F]) = SStateT[F, A, C](a => { f(a).flatMap(aa => aa match { //returns the F[(A,C)] case (aaa, b) => fc(b).f(aaa) }) }) }
  16. StateT flatMap Use the M instance to flatMap case class

    SStateT[F[_], A, B](f: (A => F[(A, B)])) { def flatMap[C](fc: B => SStateT[F, A, C]) (implicit M: Monad[F]) = SStateT[F, A, C](a => { f(a).flatMap(aa => aa match { //returns the F[(A,C)] case (aaa, b) => fc(b).f(aaa) }) }) }
  17. StateT flatMap Use the M instance to flatMap case class

    SStateT[F[_], A, B](f: (A => F[(A, B)])) { def flatMap[C](fc: B => SStateT[F, A, C]) (implicit M: Monad[F]) = SStateT[F, A, C](a => { f(a).flatMap(aa => aa match { //returns the F[(A,C)] case (aaa, b) => fc(b).f(aaa) }) }) }
  18. StateT flatMap Use the M instance to flatMap case class

    SStateT[F[_], A, B](f: (A => F[(A, B)])) { def flatMap[C](fc: B => SStateT[F, A, C]) (implicit M: Monad[F]) = SStateT[F, A, C](a => { f(a).flatMap(aa => aa match { //returns the F[(A,C)] case (aaa, b) => fc(b).f(aaa) }) }) }
  19. StateT Example: Counter def countState = SStateT[Future, Int, String]( (i:

    Int) => Future { (i+1, i.toString) }) for { a <- countState b <- countState } yield (a + b).toUpperCase
  20. An API with StateT case class CachedState(map: IMap[Int, Item]) def

    viewItem(i: Int): StateT[Task, CachedState, Item] = ??? def lockItem(i: Int): StateT[Task, CachedState, Unit] = ??? def updateItemName(id: Int, name: String): StateT[Task, CachedState, Item] = ??? def commitChange(): StateT[Task, CachedState, Unit] = ???
  21. An API with StateT case class CachedState(map: IMap[Int, Item]) def

    viewItem(i: Int): StateT[Task, CachedState, Item] = ??? def lockItem(i: Int): StateT[Task, CachedState, Unit] = ??? def updateItemName(id: Int, name: String): StateT[Task, CachedState, Item] = ??? def commitChange(): StateT[Task, CachedState, Unit] = ???
  22. An API with StateT case class CachedState(map: IMap[Int, Item]) def

    viewItem(i: Int): StateT[Task, CachedState, Item] = ??? def lockItem(i: Int): StateT[Task, CachedState, Unit] = ??? def updateItemName(id: Int, name: String): StateT[Task, CachedState, Item] = ??? def commitChange(): StateT[Task, CachedState, Unit] = ???
  23. An API with StateT case class CachedState(map: IMap[Int, Item]) def

    viewItem(i: Int): StateT[Task, CachedState, Item] = ??? def lockItem(i: Int): StateT[Task, CachedState, Unit] = ??? def updateItemName(id: Int, name: String): StateT[Task, CachedState, Item] = ??? def commitChange(): StateT[Task, CachedState, Unit] = ???
  24. An API with StateT case class CachedState(map: IMap[Int, Item]) def

    viewItem(i: Int): StateT[Task, CachedState, Item] = ??? def lockItem(i: Int): StateT[Task, CachedState, Unit] = ??? def updateItemName(id: Int, name: String): StateT[Task, CachedState, Item] = ??? def commitChange(): StateT[Task, CachedState, Unit] = ???
  25. def viewItem(i: Int): StateT[Task, CachedState, Item] = StateT((cis: CachedState) =>

    Task { cis.map.lookup(i) match { case Some(item) => (cis, item) case None => val item = viewItemUnsafe(i) val nmap = cis.map + (i -> item) (CachedState(nmap), item) } })
  26. Usage of our API for { a <- viewItem(4) _

    <- lockItem(4) _ <- updateItemName(4, "asdf") } yield (a)
  27. Sadly this compiles! for { a <- viewItem(4) _ <-

    updateItemName(4, "asdf") _ <- lockItem(4) } yield (a)
  28. Indexed StateT flatMap case class ISStateT[F[_], -A, B, C](f: (A

    => F[(B, C)])) { def flatMap[D, E](fc: C => ISStateT[F, B, D, E])(implicit M: Monad[F]): ISStateT[F, A, D, E] = ISStateT(a => { f(a).flatMap(t => t match { case (b, c) => fc(c).f(b) }) }) }
  29. Indexed StateT flatMap case class ISStateT[F[_], -A, B, C](f: (A

    => F[(B, C)])) { def flatMap[D, E](fc: C => ISStateT[F, B, D, E])(implicit M: Monad[F]): ISStateT[F, A, D, E] = ISStateT(a => { f(a).flatMap(t => t match { case (b, c) => fc(c).f(b) }) }) }
  30. Indexed StateT flatMap case class ISStateT[F[_], -A, B, C](f: (A

    => F[(B, C)])) { def flatMap[D, E](fc: C => ISStateT[F, B, D, E])(implicit M: Monad[F]): ISStateT[F, A, D, E] = ISStateT(a => { f(a).flatMap(t => t match { case (b, c) => fc(c).f(b) }) }) }
  31. Indexed StateT flatMap case class ISStateT[F[_], -A, B, C](f: (A

    => F[(B, C)])) { def flatMap[D, E](fc: C => ISStateT[F, B, D, E])(implicit M: Monad[F]): ISStateT[F, A, D, E] = ISStateT(a => { f(a).flatMap(t => t match { case (b, c) => fc(c).f(b) }) }) }
  32. Let’s model more ‘states’ type Item = String case class

    CacheState(map: IMap[Int, Item]) class LockedState(map: IMap[Int, Item]) extends CacheState(map) class UpdatedState(map: IMap[Int, Item]) extends LockedState(map)
  33. Our new API def viewItem(i: Int): IndexedStateT[Task, CacheState, CacheState, Item]

    = ??? def lockItem(i: Int): IndexedStateT[Task, CacheState, LockedState, Unit] = ??? def updateItemName(id: Int, item: Item): IndexedStateT[Task, LockedState, UpdatedState, Item] = ??? def commitChange(): IndexedStateT[Task, UpdatedState, CacheState, Unit] = ???
  34. Our new API def viewItem(i: Int): IndexedStateT[Task, CacheState, CacheState, Item]

    = ??? def lockItem(i: Int): IndexedStateT[Task, CacheState, LockedState, Unit] = ??? def updateItemName(id: Int, item: Item): IndexedStateT[Task, LockedState, UpdatedState, Item] = ??? def commitChange(): IndexedStateT[Task, UpdatedState, CacheState, Unit] = ???
  35. Our new API def viewItem(i: Int): IndexedStateT[Task, CacheState, CacheState, Item]

    = ??? def lockItem(i: Int): IndexedStateT[Task, CacheState, LockedState, Unit] = ??? def updateItemName(id: Int, item: Item): IndexedStateT[Task, LockedState, UpdatedState, Item] = ??? def commitChange(): IndexedStateT[Task, UpdatedState, CacheState, Unit] = ???
  36. Our new API def viewItem(i: Int): IndexedStateT[Task, CacheState, CacheState, Item]

    = ??? def lockItem(i: Int): IndexedStateT[Task, CacheState, LockedState, Unit] = ??? def updateItemName(id: Int, item: Item): IndexedStateT[Task, LockedState, UpdatedState, Item] = ??? def commitChange(): IndexedStateT[Task, UpdatedState, CacheState, Unit] = ???
  37. Our new API def viewItem(i: Int): IndexedStateT[Task, CacheState, CacheState, Item]

    = ??? def lockItem(i: Int): IndexedStateT[Task, CacheState, LockedState, Unit] = ??? def updateItemName(id: Int, item: Item): IndexedStateT[Task, LockedState, UpdatedState, Item] = ??? def commitChange(): IndexedStateT[Task, UpdatedState, CacheState, Unit] = ???
  38. Now… for { a <- viewItem(4) _ <- updateItemName(4, "asdf")

    _ <- lockItem(4) } yield (a) Has the following error: found : IndexedStateT[Task,cmd5.LockedState,LockedState, Item] (which expands to) IndexedStateT[Task, cmd5.LockedState,LockedState,String] required: IndexedStateT[Task,cmd4.CachedState,?,?] _ <- updateItemName(4, “asdf") We need a regular CachedState, not a Locked State!
 View Item doesn’t return a LockedItem
  39. Explaining the error
 
 found : IndexedStateT[Task,cmd5.LockedState,LockedState, Item] (which expands

    to) IndexedStateT[Task, cmd5.LockedState,LockedState,String] required: IndexedStateT[Task,cmd4.CachedState,?,?] _ <- updateItemName(4, “asdf")
 
 We can’t pass in a CacheState to something expecting a LockedState, so the types don’t work out!