Share
Embed
Transcript
39 slides
-
Have you ever wondered about the Scalaz State? found :
scalaz.State[Int,String] (which expands to) scalaz.IndexedStateT[scalaz.Id.Id,Int,Int,String] -
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))) … -
! 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) ! ! ! -
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)! })! }! ! ! -
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)! })! }! ! ! -
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)! })! }! ! ! -
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)! })! }! ! ! -
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)! })! }! ! ! -
State example: Counter ! ! def countState = SState[Int, String]((i:
Int) => ! ! ! (i+1, i.toString))! ! for {! a <- countState! b <- countState! } yield (a + b).toUpperCase! ! -
StateT time Tuple is inside the Context ! ! case
class SStateT[F[_], A, B](f: (A => F[(A, B)]))! ! -
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)! ! })! ! ! ! })! ! }! -
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)! ! })! ! ! ! })! ! }! -
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)! ! })! ! ! ! })! ! }! -
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)! ! })! ! ! ! })! ! }! -
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 -
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] = ???! ! -
! 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)! }! }) -
Usage of our API for {! a <- viewItem(4)! _
<- lockItem(4)! _ <- updateItemName(4, "asdf")! } yield (a)! -
Sadly this compiles! ! for {! a <- viewItem(4)! _
<- updateItemName(4, "asdf")! _ <- lockItem(4)! } yield (a)! ! -
! 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) }) }) } ! ! ! -
! 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) }) }) } ! ! ! -
! 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) }) }) } ! ! ! -
! 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) }) }) } ! ! ! -
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) -
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] = ???! ! -
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 -
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!