-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Convert StateT to IndexedStateT (#1775)
* Convert StateT to IndexedStateT Resolves #1773. * Add doctest for transformF * Add law checking for applicative * Move common StateT constructors to separate trait * Remove explicit `ap` override in StateT's Alternative instance * Add previous `StateT` documentation to type alias * Make IndexedStateTAlternative extend IndexedStateTMonad * Downgrade contramap and dimap to Applicative on F * Downgrade contramap and dimap to Functor on F * Specify full intersection type for returned Alternative instance * Add Bifunctor instance * Convert instance traits to abstract classes * Refine comment on IndexedStateT * Add Strong and Arrow instances for IndexedStateT * Rename files * Remove arrow instance
- Loading branch information
1 parent
75bc24a
commit 5bb0252
Showing
8 changed files
with
787 additions
and
580 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,375 @@ | ||
package cats | ||
package data | ||
|
||
import cats.functor.{ Contravariant, Bifunctor, Profunctor, Strong } | ||
import cats.syntax.either._ | ||
|
||
/** | ||
* | ||
* `IndexedStateT[F, SA, SB, A]` is a stateful computation in a context `F` yielding | ||
* a value of type `A`. The state transitions from a value of type `SA` to a value | ||
* of type `SB`. | ||
* | ||
* Note that for the `SA != SB` case, this is an indexed monad. Indexed monads | ||
* are monadic type constructors annotated by an additional type for effect | ||
* tracking purposes. In this case, the annotation tracks the initial state and | ||
* the resulting state. | ||
* | ||
* Given `IndexedStateT[F, S, S, A]`, this yields the `StateT[F, S, A]` monad. | ||
*/ | ||
final class IndexedStateT[F[_], SA, SB, A](val runF: F[SA => F[(SB, A)]]) extends Serializable { | ||
|
||
def flatMap[B, SC](fas: A => IndexedStateT[F, SB, SC, B])(implicit F: FlatMap[F]): IndexedStateT[F, SA, SC, B] = | ||
IndexedStateT.applyF(F.map(runF) { safsba => | ||
safsba.andThen { fsba => | ||
F.flatMap(fsba) { case (sb, a) => | ||
fas(a).run(sb) | ||
} | ||
} | ||
}) | ||
|
||
def flatMapF[B](faf: A => F[B])(implicit F: FlatMap[F]): IndexedStateT[F, SA, SB, B] = | ||
IndexedStateT.applyF(F.map(runF) { sfsa => | ||
sfsa.andThen { fsa => | ||
F.flatMap(fsa) { case (s, a) => F.map(faf(a))((s, _)) } | ||
} | ||
}) | ||
|
||
def map[B](f: A => B)(implicit F: Functor[F]): IndexedStateT[F, SA, SB, B] = | ||
transform { case (s, a) => (s, f(a)) } | ||
|
||
def contramap[S0](f: S0 => SA)(implicit F: Functor[F]): IndexedStateT[F, S0, SB, A] = | ||
IndexedStateT.applyF { | ||
F.map(runF) { safsba => | ||
(s0: S0) => safsba(f(s0)) | ||
} | ||
} | ||
|
||
def bimap[SC, B](f: SB => SC, g: A => B)(implicit F: Functor[F]): IndexedStateT[F, SA, SC, B] = | ||
transform { (s, a) => (f(s), g(a)) } | ||
|
||
def dimap[S0, S1](f: S0 => SA)(g: SB => S1)(implicit F: Functor[F]): IndexedStateT[F, S0, S1, A] = | ||
contramap(f).modify(g) | ||
|
||
/** | ||
* Run with the provided initial state value | ||
*/ | ||
def run(initial: SA)(implicit F: FlatMap[F]): F[(SB, A)] = | ||
F.flatMap(runF)(f => f(initial)) | ||
|
||
/** | ||
* Run with the provided initial state value and return the final state | ||
* (discarding the final value). | ||
*/ | ||
def runS(s: SA)(implicit F: FlatMap[F]): F[SB] = F.map(run(s))(_._1) | ||
|
||
/** | ||
* Run with the provided initial state value and return the final value | ||
* (discarding the final state). | ||
*/ | ||
def runA(s: SA)(implicit F: FlatMap[F]): F[A] = F.map(run(s))(_._2) | ||
|
||
/** | ||
* Run with `S`'s empty monoid value as the initial state. | ||
*/ | ||
def runEmpty(implicit S: Monoid[SA], F: FlatMap[F]): F[(SB, A)] = run(S.empty) | ||
|
||
/** | ||
* Run with `S`'s empty monoid value as the initial state and return the final | ||
* state (discarding the final value). | ||
*/ | ||
def runEmptyS(implicit S: Monoid[SA], F: FlatMap[F]): F[SB] = runS(S.empty) | ||
|
||
/** | ||
* Run with `S`'s empty monoid value as the initial state and return the final | ||
* value (discarding the final state). | ||
*/ | ||
def runEmptyA(implicit S: Monoid[SA], F: FlatMap[F]): F[A] = runA(S.empty) | ||
|
||
/** | ||
* Like [[map]], but also allows the state (`S`) value to be modified. | ||
*/ | ||
def transform[B, SC](f: (SB, A) => (SC, B))(implicit F: Functor[F]): IndexedStateT[F, SA, SC, B] = | ||
IndexedStateT.applyF( | ||
F.map(runF) { sfsa => | ||
sfsa.andThen { fsa => | ||
F.map(fsa) { case (s, a) => f(s, a) } | ||
} | ||
}) | ||
|
||
/** | ||
* Like [[transform]], but allows the context to change from `F` to `G`. | ||
* | ||
* {{{ | ||
* scala> import cats.implicits._ | ||
* scala> type ErrorOr[A] = Either[String, A] | ||
* scala> val xError: IndexedStateT[ErrorOr, Int, Int, Int] = IndexedStateT.get | ||
* scala> val xOpt: IndexedStateT[Option, Int, Int, Int] = xError.transformF(_.toOption) | ||
* scala> val input = 5 | ||
* scala> xError.run(input) | ||
* res0: ErrorOr[(Int, Int)] = Right((5,5)) | ||
* scala> xOpt.run(5) | ||
* res1: Option[(Int, Int)] = Some((5,5)) | ||
* }}} | ||
*/ | ||
def transformF[G[_], B, SC](f: F[(SB, A)] => G[(SC, B)])(implicit F: FlatMap[F], G: Applicative[G]): IndexedStateT[G, SA, SC, B] = | ||
IndexedStateT(s => f(run(s))) | ||
|
||
/** | ||
* Transform the state used. | ||
* | ||
* This is useful when you are working with many focused `StateT`s and want to pass in a | ||
* global state containing the various states needed for each individual `StateT`. | ||
* | ||
* {{{ | ||
* scala> import cats.implicits._ // needed for StateT.apply | ||
* scala> type GlobalEnv = (Int, String) | ||
* scala> val x: StateT[Option, Int, Double] = StateT((x: Int) => Option((x + 1, x.toDouble))) | ||
* scala> val xt: StateT[Option, GlobalEnv, Double] = x.transformS[GlobalEnv](_._1, (t, i) => (i, t._2)) | ||
* scala> val input = 5 | ||
* scala> x.run(input) | ||
* res0: Option[(Int, Double)] = Some((6,5.0)) | ||
* scala> xt.run((input, "hello")) | ||
* res1: Option[(GlobalEnv, Double)] = Some(((6,hello),5.0)) | ||
* }}} | ||
*/ | ||
def transformS[R](f: R => SA, g: (R, SB) => R)(implicit F: Functor[F]): IndexedStateT[F, R, R, A] = | ||
StateT.applyF(F.map(runF) { sfsa => | ||
{ r: R => | ||
val sa = f(r) | ||
val fsba = sfsa(sa) | ||
F.map(fsba) { case (sb, a) => (g(r, sb), a) } | ||
} | ||
}) | ||
|
||
/** | ||
* Modify the state (`S`) component. | ||
*/ | ||
def modify[SC](f: SB => SC)(implicit F: Functor[F]): IndexedStateT[F, SA, SC, A] = | ||
transform((s, a) => (f(s), a)) | ||
|
||
/** | ||
* Inspect a value from the input state, without modifying the state. | ||
*/ | ||
def inspect[B](f: SB => B)(implicit F: Functor[F]): IndexedStateT[F, SA, SB, B] = | ||
transform((s, _) => (s, f(s))) | ||
|
||
/** | ||
* Get the input state, without modifying the state. | ||
*/ | ||
def get(implicit F: Functor[F]): IndexedStateT[F, SA, SB, SB] = | ||
inspect(identity) | ||
} | ||
|
||
private[data] trait CommonStateTConstructors { | ||
def pure[F[_], S, A](a: A)(implicit F: Applicative[F]): IndexedStateT[F, S, S, A] = | ||
IndexedStateT(s => F.pure((s, a))) | ||
|
||
def lift[F[_], S, A](fa: F[A])(implicit F: Applicative[F]): IndexedStateT[F, S, S, A] = | ||
IndexedStateT(s => F.map(fa)(a => (s, a))) | ||
|
||
def inspect[F[_], S, A](f: S => A)(implicit F: Applicative[F]): IndexedStateT[F, S, S, A] = | ||
IndexedStateT(s => F.pure((s, f(s)))) | ||
|
||
def inspectF[F[_], S, A](f: S => F[A])(implicit F: Applicative[F]): IndexedStateT[F, S, S, A] = | ||
IndexedStateT(s => F.map(f(s))(a => (s, a))) | ||
|
||
def get[F[_], S](implicit F: Applicative[F]): IndexedStateT[F, S, S, S] = | ||
IndexedStateT(s => F.pure((s, s))) | ||
} | ||
|
||
object IndexedStateT extends IndexedStateTInstances with CommonStateTConstructors { | ||
def apply[F[_], SA, SB, A](f: SA => F[(SB, A)])(implicit F: Applicative[F]): IndexedStateT[F, SA, SB, A] = | ||
new IndexedStateT(F.pure(f)) | ||
|
||
def applyF[F[_], SA, SB, A](runF: F[SA => F[(SB, A)]]): IndexedStateT[F, SA, SB, A] = | ||
new IndexedStateT(runF) | ||
|
||
def modify[F[_], SA, SB](f: SA => SB)(implicit F: Applicative[F]): IndexedStateT[F, SA, SB, Unit] = | ||
IndexedStateT(sa => F.pure((f(sa), ()))) | ||
|
||
def modifyF[F[_], SA, SB](f: SA => F[SB])(implicit F: Applicative[F]): IndexedStateT[F, SA, SB, Unit] = | ||
IndexedStateT(s => F.map(f(s))(s => (s, ()))) | ||
|
||
def set[F[_], SA, SB](sb: SB)(implicit F: Applicative[F]): IndexedStateT[F, SA, SB, Unit] = | ||
IndexedStateT(_ => F.pure((sb, ()))) | ||
|
||
def setF[F[_], SA, SB](fsb: F[SB])(implicit F: Applicative[F]): IndexedStateT[F, SA, SB, Unit] = | ||
IndexedStateT(_ => F.map(fsb)(s => (s, ()))) | ||
} | ||
|
||
private[data] abstract class StateTFunctions extends CommonStateTConstructors { | ||
def apply[F[_], S, A](f: S => F[(S, A)])(implicit F: Applicative[F]): StateT[F, S, A] = | ||
IndexedStateT(f) | ||
|
||
def applyF[F[_], S, A](runF: F[S => F[(S, A)]]): StateT[F, S, A] = | ||
IndexedStateT.applyF(runF) | ||
|
||
def modify[F[_], S](f: S => S)(implicit F: Applicative[F]): StateT[F, S, Unit] = | ||
apply(sa => F.pure((f(sa), ()))) | ||
|
||
def modifyF[F[_], S](f: S => F[S])(implicit F: Applicative[F]): StateT[F, S, Unit] = | ||
apply(s => F.map(f(s))(s => (s, ()))) | ||
|
||
def set[F[_], S](s: S)(implicit F: Applicative[F]): StateT[F, S, Unit] = | ||
apply(_ => F.pure((s, ()))) | ||
|
||
def setF[F[_], S](fs: F[S])(implicit F: Applicative[F]): StateT[F, S, Unit] = | ||
apply(_ => F.map(fs)(s => (s, ()))) | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTInstances extends IndexedStateTInstances1 { | ||
implicit def catsDataAlternativeForIndexedStateT[F[_], S](implicit FM: Monad[F], | ||
FA: Alternative[F]): Alternative[IndexedStateT[F, S, S, ?]] with Monad[IndexedStateT[F, S, S, ?]] = | ||
new IndexedStateTAlternative[F, S] { implicit def F = FM; implicit def G = FA } | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTInstances1 extends IndexedStateTInstances2 { | ||
implicit def catsDataMonadErrorForIndexedStateT[F[_], S, E](implicit F0: MonadError[F, E]): MonadError[IndexedStateT[F, S, S, ?], E] = | ||
new IndexedStateTMonadError[F, S, E] { implicit def F = F0 } | ||
|
||
implicit def catsDataSemigroupKForIndexedStateT[F[_], SA, SB](implicit F0: Monad[F], G0: SemigroupK[F]): SemigroupK[IndexedStateT[F, SA, SB, ?]] = | ||
new IndexedStateTSemigroupK[F, SA, SB] { implicit def F = F0; implicit def G = G0 } | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTInstances2 extends IndexedStateTInstances3 { | ||
implicit def catsDataMonadForIndexedStateT[F[_], S](implicit F0: Monad[F]): Monad[IndexedStateT[F, S, S, ?]] = | ||
new IndexedStateTMonad[F, S] { implicit def F = F0 } | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTInstances3 extends IndexedStateTInstances4 { | ||
implicit def catsDataFunctorForIndexedStateT[F[_], SA, SB](implicit F0: Functor[F]): Functor[IndexedStateT[F, SA, SB, ?]] = | ||
new IndexedStateTFunctor[F, SA, SB] { implicit def F = F0 } | ||
|
||
implicit def catsDataContravariantForIndexedStateT[F[_], SB, V](implicit F0: Functor[F]): Contravariant[IndexedStateT[F, ?, SB, V]] = | ||
new IndexedStateTContravariant[F, SB, V] { implicit def F = F0 } | ||
|
||
implicit def catsDataProfunctorForIndexedStateT[F[_], V](implicit F0: Functor[F]): Profunctor[IndexedStateT[F, ?, ?, V]] = | ||
new IndexedStateTProfunctor[F, V] { implicit def F = F0 } | ||
|
||
implicit def catsDataBifunctorForIndexedStateT[F[_], SA](implicit F0: Functor[F]): Bifunctor[IndexedStateT[F, SA, ?, ?]] = | ||
new IndexedStateTBifunctor[F, SA] { implicit def F = F0 } | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTInstances4 { | ||
implicit def catsDataStrongForIndexedStateT[F[_], V](implicit F0: Monad[F]): Strong[IndexedStateT[F, ?, ?, V]] = | ||
new IndexedStateTStrong[F, V] { implicit def F = F0 } | ||
} | ||
|
||
// To workaround SI-7139 `object State` needs to be defined inside the package object | ||
// together with the type alias. | ||
private[data] abstract class StateFunctions { | ||
|
||
def apply[S, A](f: S => (S, A)): State[S, A] = | ||
IndexedStateT.applyF(Now((s: S) => Now(f(s)))) | ||
|
||
/** | ||
* Return `a` and maintain the input state. | ||
*/ | ||
def pure[S, A](a: A): State[S, A] = State(s => (s, a)) | ||
|
||
/** | ||
* Modify the input state and return Unit. | ||
*/ | ||
def modify[S](f: S => S): State[S, Unit] = State(s => (f(s), ())) | ||
|
||
/** | ||
* Inspect a value from the input state, without modifying the state. | ||
*/ | ||
def inspect[S, T](f: S => T): State[S, T] = State(s => (s, f(s))) | ||
|
||
/** | ||
* Return the input state without modifying it. | ||
*/ | ||
def get[S]: State[S, S] = inspect(identity) | ||
|
||
/** | ||
* Set the state to `s` and return Unit. | ||
*/ | ||
def set[S](s: S): State[S, Unit] = State(_ => (s, ())) | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTFunctor[F[_], SA, SB] extends Functor[IndexedStateT[F, SA, SB, ?]] { | ||
implicit def F: Functor[F] | ||
|
||
override def map[A, B](fa: IndexedStateT[F, SA, SB, A])(f: A => B): IndexedStateT[F, SA, SB, B] = | ||
fa.map(f) | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTContravariant[F[_], SB, V] extends Contravariant[IndexedStateT[F, ?, SB, V]] { | ||
implicit def F: Functor[F] | ||
|
||
override def contramap[A, B](fa: IndexedStateT[F, A, SB, V])(f: B => A): IndexedStateT[F, B, SB, V] = | ||
fa.contramap(f) | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTBifunctor[F[_], SA] extends Bifunctor[IndexedStateT[F, SA, ?, ?]] { | ||
implicit def F: Functor[F] | ||
|
||
def bimap[A, B, C, D](fab: IndexedStateT[F, SA, A, B])(f: A => C, g: B => D): IndexedStateT[F, SA, C, D] = | ||
fab.bimap(f, g) | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTProfunctor[F[_], V] extends Profunctor[IndexedStateT[F, ?, ?, V]] { | ||
implicit def F: Functor[F] | ||
|
||
def dimap[A, B, C, D](fab: IndexedStateT[F, A, B, V])(f: C => A)(g: B => D): IndexedStateT[F, C, D, V] = | ||
fab.dimap(f)(g) | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTStrong[F[_], V] extends IndexedStateTProfunctor[F, V] with Strong[IndexedStateT[F, ?, ?, V]] { | ||
implicit def F: Monad[F] | ||
|
||
def first[A, B, C](fa: IndexedStateT[F, A, B, V]): IndexedStateT[F, (A, C), (B, C), V] = | ||
IndexedStateT { case (a, c) => | ||
F.map(fa.run(a)) { case (b, v) => | ||
((b, c), v) | ||
} | ||
} | ||
|
||
def second[A, B, C](fa: IndexedStateT[F, A, B, V]): IndexedStateT[F, (C, A), (C, B), V] = | ||
first(fa).dimap((_: (C, A)).swap)(_.swap) | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTMonad[F[_], S] extends IndexedStateTFunctor[F, S, S] with Monad[IndexedStateT[F, S, S, ?]] { | ||
implicit def F: Monad[F] | ||
|
||
def pure[A](a: A): IndexedStateT[F, S, S, A] = | ||
IndexedStateT.pure(a) | ||
|
||
def flatMap[A, B](fa: IndexedStateT[F, S, S, A])(f: A => IndexedStateT[F, S, S, B]): IndexedStateT[F, S, S, B] = | ||
fa.flatMap(f) | ||
|
||
def tailRecM[A, B](a: A)(f: A => IndexedStateT[F, S, S, Either[A, B]]): IndexedStateT[F, S, S, B] = | ||
IndexedStateT[F, S, S, B](s => F.tailRecM[(S, A), (S, B)]((s, a)) { | ||
case (s, a) => F.map(f(a).run(s)) { case (s, ab) => ab.bimap((s, _), (s, _)) } | ||
}) | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTSemigroupK[F[_], SA, SB] extends SemigroupK[IndexedStateT[F, SA, SB, ?]] { | ||
implicit def F: Monad[F] | ||
implicit def G: SemigroupK[F] | ||
|
||
def combineK[A](x: IndexedStateT[F, SA, SB, A], y: IndexedStateT[F, SA, SB, A]): IndexedStateT[F, SA, SB, A] = | ||
IndexedStateT(s => G.combineK(x.run(s), y.run(s))) | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTAlternative[F[_], S] extends IndexedStateTMonad[F, S] with Alternative[IndexedStateT[F, S, S, ?]] { | ||
def G: Alternative[F] | ||
|
||
def combineK[A](x: IndexedStateT[F, S, S, A], y: IndexedStateT[F, S, S, A]): IndexedStateT[F, S, S, A] = | ||
IndexedStateT[F, S, S, A](s => G.combineK(x.run(s), y.run(s)))(G) | ||
|
||
def empty[A]: IndexedStateT[F, S, S, A] = | ||
IndexedStateT.lift[F, S, A](G.empty[A])(G) | ||
} | ||
|
||
private[data] sealed abstract class IndexedStateTMonadError[F[_], S, E] extends IndexedStateTMonad[F, S] | ||
with MonadError[IndexedStateT[F, S, S, ?], E] { | ||
implicit def F: MonadError[F, E] | ||
|
||
def raiseError[A](e: E): IndexedStateT[F, S, S, A] = IndexedStateT.lift(F.raiseError(e)) | ||
|
||
def handleErrorWith[A](fa: IndexedStateT[F, S, S, A])(f: E => IndexedStateT[F, S, S, A]): IndexedStateT[F, S, S, A] = | ||
IndexedStateT(s => F.handleErrorWith(fa.run(s))(e => f(e).run(s))) | ||
} |
Oops, something went wrong.