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

Scale By the Bay 2017 Index your State

Scale By the Bay 2017 Index your State

Vincent Marquez

November 22, 2017
Tweet

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!