From c001d9c26dac739d3ffdfa53357165237649c411 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Sat, 29 Jul 2017 22:53:44 +0300 Subject: [PATCH] Convert StateT to IndexedStateT Resolves #1773. --- core/src/main/scala/cats/data/StateT.scala | 229 ++++++++++++------ core/src/main/scala/cats/data/package.scala | 3 + .../src/test/scala/cats/free/FreeTTests.scala | 4 +- .../cats/laws/discipline/Arbitrary.scala | 6 +- .../src/test/scala/cats/tests/MonadTest.scala | 4 +- .../test/scala/cats/tests/StateTTests.scala | 117 ++++++--- 6 files changed, 244 insertions(+), 119 deletions(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 82c5d21f3e2..fe90559e1b8 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -1,75 +1,90 @@ package cats package data +import cats.functor.{ Contravariant, Profunctor } import cats.syntax.either._ /** - * `StateT[F, S, A]` is similar to `Kleisli[F, S, A]` in that it takes an `S` - * argument and produces an `A` value wrapped in `F`. However, it also produces - * an `S` value representing the updated state (which is wrapped in the `F` - * context along with the `A` value. + * + * `IndexedStateT[F, SA, SB, A]` is a stateful computation in a context `F` yielding + * a value of type `A`. Its state transitions from a value of type `SA` to a value + * of type `SB`. + * + * Given `IndexedStateT[F, S, S, A]`, this yields the plain `StateT[F, S, A]`. + * + * Note that `IndexedStateT[F, SA, SB, A]` is not a monad, but an indexed monad. */ -final class StateT[F[_], S, A](val runF: F[S => F[(S, A)]]) extends Serializable { +final class IndexedStateT[F[_], SA, SB, A](val runF: F[SA => F[(SB, A)]]) extends Serializable { - def flatMap[B](fas: A => StateT[F, S, B])(implicit F: FlatMap[F]): StateT[F, S, B] = - StateT.applyF(F.map(runF) { sfsa => - sfsa.andThen { fsa => - F.flatMap(fsa) { case (s, a) => - fas(a).run(s) + 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]): StateT[F, S, B] = - StateT.applyF(F.map(runF) { sfsa => + 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]): StateT[F, S, B] = + 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: Monad[F]): IndexedStateT[F, S0, SB, A] = + IndexedStateT.apply { s0 => + F.flatMap(runF) { safsba => + safsba(f(s0)) + } + } + + def dimap[S0, S1](f: S0 => SA)(g: SB => S1)(implicit F: Monad[F]): IndexedStateT[F, S0, S1, A] = + contramap(f).modify(g) + /** * Run with the provided initial state value */ - def run(initial: S)(implicit F: FlatMap[F]): F[(S, A)] = + 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: S)(implicit F: FlatMap[F]): F[S] = F.map(run(s))(_._1) + 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: S)(implicit F: FlatMap[F]): F[A] = F.map(run(s))(_._2) + 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[S], F: FlatMap[F]): F[(S, A)] = run(S.empty) + 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[S], F: FlatMap[F]): F[S] = runS(S.empty) + 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[S], F: FlatMap[F]): F[A] = runA(S.empty) + 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](f: (S, A) => (S, B))(implicit F: Functor[F]): StateT[F, S, B] = - StateT.applyF( + 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) } @@ -79,8 +94,8 @@ final class StateT[F[_], S, A](val runF: F[S => F[(S, A)]]) extends Serializable /** * Like [[transform]], but allows the context to change from `F` to `G`. */ - def transformF[G[_], B](f: F[(S, A)] => G[(S, B)])(implicit F: FlatMap[F], G: Applicative[G]): StateT[G, S, B] = - StateT(s => f(run(s))) + 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. @@ -100,90 +115,131 @@ final class StateT[F[_], S, A](val runF: F[S => F[(S, A)]]) extends Serializable * res1: Option[(GlobalEnv, Double)] = Some(((6,hello),5.0)) * }}} */ - def transformS[R](f: R => S, g: (R, S) => R)(implicit F: Functor[F]): StateT[F, R, A] = + 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 s = f(r) - val fsa = sfsa(s) - F.map(fsa) { case (s, a) => (g(r, s), a) } + 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(f: S => S)(implicit F: Functor[F]): StateT[F, S, A] = + 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: S => B)(implicit F: Functor[F]): StateT[F, S, B] = + 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]): StateT[F, S, S] = + def get(implicit F: Functor[F]): IndexedStateT[F, SA, SB, SB] = inspect(identity) } -object StateT extends StateTInstances { +object IndexedStateT extends IndexedStateTInstances { + 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 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 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 get[F[_], S](implicit F: Applicative[F]): IndexedStateT[F, S, S, S] = + IndexedStateT(s => F.pure((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 { def apply[F[_], S, A](f: S => F[(S, A)])(implicit F: Applicative[F]): StateT[F, S, A] = - new StateT(F.pure(f)) + IndexedStateT(f) def applyF[F[_], S, A](runF: F[S => F[(S, A)]]): StateT[F, S, A] = - new StateT(runF) + IndexedStateT.applyF(runF) def pure[F[_], S, A](a: A)(implicit F: Applicative[F]): StateT[F, S, A] = - StateT(s => F.pure((s, a))) + apply(s => F.pure((s, a))) def lift[F[_], S, A](fa: F[A])(implicit F: Applicative[F]): StateT[F, S, A] = - StateT(s => F.map(fa)(a => (s, a))) + apply(s => F.map(fa)(a => (s, a))) def inspect[F[_], S, A](f: S => A)(implicit F: Applicative[F]): StateT[F, S, A] = - StateT(s => F.pure((s, f(s)))) + apply(s => F.pure((s, f(s)))) def inspectF[F[_], S, A](f: S => F[A])(implicit F: Applicative[F]): StateT[F, S, A] = - StateT(s => F.map(f(s))(a => (s, a))) + apply(s => F.map(f(s))(a => (s, a))) def modify[F[_], S](f: S => S)(implicit F: Applicative[F]): StateT[F, S, Unit] = - StateT(s => F.pure((f(s), ()))) + apply(sa => F.pure((f(sa), ()))) def modifyF[F[_], S](f: S => F[S])(implicit F: Applicative[F]): StateT[F, S, Unit] = - StateT(s => F.map(f(s))(s => (s, ()))) + apply(s => F.map(f(s))(s => (s, ()))) def get[F[_], S](implicit F: Applicative[F]): StateT[F, S, S] = - StateT(s => F.pure((s, s))) + apply(s => F.pure((s, s))) def set[F[_], S](s: S)(implicit F: Applicative[F]): StateT[F, S, Unit] = - StateT(_ => F.pure((s, ()))) + apply(_ => F.pure((s, ()))) def setF[F[_], S](fs: F[S])(implicit F: Applicative[F]): StateT[F, S, Unit] = - StateT(_ => F.map(fs)(s => (s, ()))) + apply(_ => F.map(fs)(s => (s, ()))) } -private[data] sealed trait StateTInstances extends StateTInstances1 { - implicit def catsDataAlternativeForStateT[F[_], S](implicit FM: Monad[F], FA: Alternative[F]): Alternative[StateT[F, S, ?]] = - new StateTAlternative[F, S] { implicit def F = FM; implicit def G = FA } +private[data] sealed trait IndexedStateTInstances extends IndexedStateTInstances1 { + implicit def catsDataAlternativeForIndexedStateT[F[_], S](implicit FM: Monad[F], FA: Alternative[F]): Alternative[StateT[F, S, ?]] = + new IndexedStateTAlternative[F, S] { implicit def F = FM; implicit def G = FA } } -private[data] sealed trait StateTInstances1 extends StateTInstances2 { - implicit def catsDataMonadErrorForStateT[F[_], S, E](implicit F0: MonadError[F, E]): MonadError[StateT[F, S, ?], E] = - new StateTMonadError[F, S, E] { implicit def F = F0 } +private[data] sealed trait 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 catsDataSemigroupKForStateT[F[_], S](implicit F0: Monad[F], G0: SemigroupK[F]): SemigroupK[StateT[F, S, ?]] = - new StateTSemigroupK[F, S] { implicit def F = F0; implicit def G = G0 } + 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 trait StateTInstances2 extends StateTInstances3 { - implicit def catsDataMonadForStateT[F[_], S](implicit F0: Monad[F]): Monad[StateT[F, S, ?]] = - new StateTMonad[F, S] { implicit def F = F0 } +private[data] sealed trait 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 trait StateTInstances3 { - implicit def catsDataFunctorForStateT[F[_], S](implicit F0: Functor[F]): Functor[StateT[F, S, ?]] = - new StateTFunctor[F, S] { implicit def F = F0 } +private[data] sealed trait IndexedStateTInstances3 { + 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: Monad[F]): Contravariant[IndexedStateT[F, ?, SB, V]] = + new IndexedStateTContravariant[F, SB, V] { implicit def F = F0 } + + implicit def catsDataProfunctorForIndexedStateT[F[_], V](implicit F0: Monad[F]): Profunctor[IndexedStateT[F, ?, ?, V]] = + new IndexedStateTProfunctor[F, V] { implicit def F = F0 } } // To workaround SI-7139 `object State` needs to be defined inside the package object @@ -191,7 +247,7 @@ private[data] sealed trait StateTInstances3 { private[data] abstract class StateFunctions { def apply[S, A](f: S => (S, A)): State[S, A] = - StateT.applyF(Now((s: S) => Now(f(s)))) + IndexedStateT.applyF(Now((s: S) => Now(f(s)))) /** * Return `a` and maintain the input state. @@ -219,50 +275,66 @@ private[data] abstract class StateFunctions { def set[S](s: S): State[S, Unit] = State(_ => (s, ())) } -private[data] sealed trait StateTFunctor[F[_], S] extends Functor[StateT[F, S, ?]] { +private[data] sealed trait IndexedStateTFunctor[F[_], SA, SB] extends Functor[IndexedStateT[F, SA, SB, ?]] { implicit def F: Functor[F] - override def map[A, B](fa: StateT[F, S, A])(f: A => B): StateT[F, S, B] = fa.map(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 trait IndexedStateTContravariant[F[_], SB, V] extends Contravariant[IndexedStateT[F, ?, SB, V]] { + implicit def F: Monad[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 trait StateTMonad[F[_], S] extends Monad[StateT[F, S, ?]] with StateTFunctor[F, S] { +private[data] sealed trait IndexedStateTProfunctor[F[_], V] extends Profunctor[IndexedStateT[F, ?, ?, V]] { + implicit def F: Monad[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 trait IndexedStateTMonad[F[_], S] extends Monad[IndexedStateT[F, S, S, ?]] + with IndexedStateTFunctor[F, S, S] { implicit def F: Monad[F] def pure[A](a: A): StateT[F, S, A] = - StateT.pure(a) + IndexedStateT.pure(a) def flatMap[A, B](fa: StateT[F, S, A])(f: A => StateT[F, S, B]): StateT[F, S, B] = fa.flatMap(f) def tailRecM[A, B](a: A)(f: A => StateT[F, S, Either[A, B]]): StateT[F, S, B] = - StateT[F, S, B](s => F.tailRecM[(S, A), (S, B)]((s, a)) { + 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 trait StateTSemigroupK[F[_], S] extends SemigroupK[StateT[F, S, ?]] { +private[data] sealed trait IndexedStateTSemigroupK[F[_], SA, SB] extends SemigroupK[IndexedStateT[F, SA, SB, ?]] { implicit def F: Monad[F] implicit def G: SemigroupK[F] - def combineK[A](x: StateT[F, S, A], y: StateT[F, S, A]): StateT[F, S, A] = - StateT(s => G.combineK(x.run(s), y.run(s))) + 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 trait StateTAlternative[F[_], S] extends Alternative[StateT[F, S, ?]] with StateTFunctor[F, S] { +private[data] sealed trait IndexedStateTAlternative[F[_], S] extends Alternative[IndexedStateT[F, S, S, ?]] with IndexedStateTFunctor[F, S, S] { implicit def F: Monad[F] def G: Alternative[F] - def combineK[A](x: StateT[F, S, A], y: StateT[F, S, A]): StateT[F, S, A] = - StateT[F, S, A](s => G.combineK(x.run(s), y.run(s)))(G) + 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 pure[A](a: A): StateT[F, S, A] = - StateT.pure[F, S, A](a)(G) + def pure[A](a: A): IndexedStateT[F, S, S, A] = + IndexedStateT.pure[F, S, A](a)(G) - def empty[A]: StateT[F, S, A] = - StateT.lift[F, S, A](G.empty[A])(G) + def empty[A]: IndexedStateT[F, S, S, A] = + IndexedStateT.lift[F, S, A](G.empty[A])(G) - override def ap[A, B](ff: StateT[F, S, A => B])(fa: StateT[F, S, A]): StateT[F, S, B] = - StateT[F, S, B]((s: S) => + override def ap[A, B](ff: IndexedStateT[F, S, S, A => B])(fa: IndexedStateT[F, S, S, A]): IndexedStateT[F, S, S, B] = + IndexedStateT[F, S, S, B]((s: S) => F.flatMap(ff.run(s)) { sab => val (sn, f) = sab F.map(fa.run(sn)) { case (snn, a) => (snn, f(a)) } @@ -270,11 +342,12 @@ private[data] sealed trait StateTAlternative[F[_], S] extends Alternative[StateT ) } -private[data] sealed trait StateTMonadError[F[_], S, E] extends StateTMonad[F, S] with MonadError[StateT[F, S, ?], E] { +private[data] sealed trait 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): StateT[F, S, A] = StateT.lift(F.raiseError(e)) + def raiseError[A](e: E): IndexedStateT[F, S, S, A] = IndexedStateT.lift(F.raiseError(e)) - def handleErrorWith[A](fa: StateT[F, S, A])(f: E => StateT[F, S, A]): StateT[F, S, A] = - StateT(s => F.handleErrorWith(fa.run(s))(e => f(e).run(s))) + 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))) } diff --git a/core/src/main/scala/cats/data/package.scala b/core/src/main/scala/cats/data/package.scala index e367b1599ca..0e95a297ba3 100644 --- a/core/src/main/scala/cats/data/package.scala +++ b/core/src/main/scala/cats/data/package.scala @@ -29,6 +29,9 @@ package object data { def tell[L](l: L): Writer[L, Unit] = WriterT.tell(l) } + type StateT[F[_], S, A] = IndexedStateT[F, S, S, A] + object StateT extends StateTFunctions + type State[S, A] = StateT[Eval, S, A] object State extends StateFunctions diff --git a/free/src/test/scala/cats/free/FreeTTests.scala b/free/src/test/scala/cats/free/FreeTTests.scala index a5f2f30f1ea..51329cb8f8b 100644 --- a/free/src/test/scala/cats/free/FreeTTests.scala +++ b/free/src/test/scala/cats/free/FreeTTests.scala @@ -170,9 +170,9 @@ object FreeTTests extends FreeTTestsInstances { trait FreeTTestsInstances { import FreeT._ - import StateT._ + import IndexedStateT._ import cats.kernel.instances.option._ - import cats.tests.StateTTests._ + import cats.tests.IndexedStateTTests._ import CartesianTests._ type IntState[A] = State[Int, A] diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index 963d71faa2e..4e719bd641d 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -153,7 +153,7 @@ object arbitrary extends ArbitraryInstances0 { Arbitrary(FG.arbitrary.map(Nested(_))) implicit def catsLawArbitraryForState[S: Arbitrary: Cogen, A: Arbitrary]: Arbitrary[State[S, A]] = - catsLawArbitraryForStateT[Eval, S, A] + catsLawArbitraryForIndexedStateT[Eval, S, S, A] implicit def catsLawArbitraryForReader[A: Arbitrary: Cogen, B: Arbitrary]: Arbitrary[Reader[A, B]] = catsLawsArbitraryForKleisli[Id, A, B] @@ -168,8 +168,8 @@ object arbitrary extends ArbitraryInstances0 { private[discipline] sealed trait ArbitraryInstances0 { - implicit def catsLawArbitraryForStateT[F[_], S, A](implicit F: Arbitrary[F[S => F[(S, A)]]]): Arbitrary[StateT[F, S, A]] = - Arbitrary(F.arbitrary.map(StateT.applyF)) + implicit def catsLawArbitraryForIndexedStateT[F[_], SA, SB, A](implicit F: Arbitrary[F[SA => F[(SB, A)]]]): Arbitrary[IndexedStateT[F, SA, SB, A]] = + Arbitrary(F.arbitrary.map(IndexedStateT.applyF)) implicit def catsLawsArbitraryForWriterT[F[_], L, V](implicit F: Arbitrary[F[(L, V)]]): Arbitrary[WriterT[F, L, V]] = Arbitrary(F.arbitrary.map(WriterT(_))) diff --git a/tests/src/test/scala/cats/tests/MonadTest.scala b/tests/src/test/scala/cats/tests/MonadTest.scala index 063f389f9a8..1a1c0eae4e0 100644 --- a/tests/src/test/scala/cats/tests/MonadTest.scala +++ b/tests/src/test/scala/cats/tests/MonadTest.scala @@ -1,11 +1,11 @@ package cats package tests -import cats.data.{StateT} +import cats.data.{IndexedStateT, StateT} import org.scalacheck.Gen class MonadTest extends CatsSuite { - implicit val testInstance: Monad[StateT[Id, Int, ?]] = StateT.catsDataMonadForStateT[Id, Int] + implicit val testInstance: Monad[StateT[Id, Int, ?]] = IndexedStateT.catsDataMonadForIndexedStateT[Id, Int] val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) val incrementAndGet: StateT[Id, Int, Int] = increment >> StateT.get diff --git a/tests/src/test/scala/cats/tests/StateTTests.scala b/tests/src/test/scala/cats/tests/StateTTests.scala index 81c1ee99ede..efa21bd4aca 100644 --- a/tests/src/test/scala/cats/tests/StateTTests.scala +++ b/tests/src/test/scala/cats/tests/StateTTests.scala @@ -1,24 +1,41 @@ package cats package tests -import cats.data.{State, StateT, EitherT} +import cats.data.{State, StateT, IndexedStateT, EitherT} +import cats.functor.{Contravariant, Profunctor} import cats.kernel.instances.tuple._ import cats.laws.discipline._ import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary._ import org.scalacheck.Arbitrary -class StateTTests extends CatsSuite { +class IndexedStateTTests extends CatsSuite { implicit override val generatorDrivenConfig: PropertyCheckConfiguration = checkConfiguration.copy(sizeRange = 5) - import StateTTests._ + import IndexedStateTTests._ test("basic state usage"){ add1.run(1).value should === (2 -> 1) } + test("basic IndexedStateT usage") { + val listHead: IndexedStateT[Id, List[Int], Option[Int], Unit] = IndexedStateT.modify(_.headOption) + val getOrElse: IndexedStateT[Id, Option[Int], Int, Unit] = IndexedStateT.modify(_.getOrElse(0)) + val toString: IndexedStateT[Id, Int, String, Unit] = IndexedStateT.modify(_.toString) + + val composite = for { + _ <- listHead + _ <- getOrElse + _ <- toString + r <- IndexedStateT.get[Id, String] + } yield r + + composite.run(List(1, 2, 3)) should === (("1", "1")) + composite.run(Nil) should === (("0", "0")) + } + test("traversing state is stack-safe"){ val ns = (0 to 70000).toList val x = ns.traverse(_ => add1) @@ -199,38 +216,70 @@ class StateTTests extends CatsSuite { } - implicit val iso = CartesianTests.Isomorphisms.invariant[StateT[ListWrapper, Int, ?]](StateT.catsDataFunctorForStateT(ListWrapper.monad)) + implicit val iso = CartesianTests.Isomorphisms.invariant[IndexedStateT[ListWrapper, String, Int, ?]](IndexedStateT.catsDataFunctorForIndexedStateT(ListWrapper.monad)) { // F has a Functor implicit val F: Functor[ListWrapper] = ListWrapper.functor // We only need a Functor on F to find a Functor on StateT - Functor[StateT[ListWrapper, Int, ?]] + Functor[IndexedStateT[ListWrapper, String, Int, ?]] + } + + { + // We only need a Monad to derive a Contravariant for IndexedStateT + implicit val F: Monad[ListWrapper] = ListWrapper.monad + Contravariant[IndexedStateT[ListWrapper, ?, Int, String]] + } + + { + // We only need a Monad to derive a Profunctor for IndexedStateT + implicit val F: Monad[ListWrapper] = ListWrapper.monad + Profunctor[IndexedStateT[ListWrapper, ?, ?, String]] } { // F needs a Monad to do Eq on StateT implicit val F: Monad[ListWrapper] = ListWrapper.monad - implicit val FS: Functor[StateT[ListWrapper, Int, ?]] = StateT.catsDataFunctorForStateT + implicit val FS: Functor[IndexedStateT[ListWrapper, String, Int, ?]] = IndexedStateT.catsDataFunctorForIndexedStateT + + checkAll("IndexedStateT[ListWrapper, String, Int, Int]", FunctorTests[IndexedStateT[ListWrapper, String, Int, ?]].functor[Int, Int, Int]) + checkAll("Functor[IndexedStateT[ListWrapper, Int, ?]]", SerializableTests.serializable(Functor[IndexedStateT[ListWrapper, String, Int, ?]])) + + Functor[IndexedStateT[ListWrapper, String, Int, ?]] + } + + { + implicit val F: Monad[ListWrapper] = ListWrapper.monad + implicit val FS: Contravariant[IndexedStateT[ListWrapper, ?, Int, Int]] = IndexedStateT.catsDataContravariantForIndexedStateT + + checkAll("IndexedStateT[ListWrapper, ?, Int, Int]", ContravariantTests[IndexedStateT[ListWrapper, ?, Int, Int]].contravariant[Int, Int, Int]) + checkAll("Contravariant[IndexedStateT[ListWrapper, ?, Int, Int]]", SerializableTests.serializable(Contravariant[IndexedStateT[ListWrapper, ?, Int, Int]])) + + Contravariant[IndexedStateT[ListWrapper, ?, Int, Int]] + } + + { + implicit val F: Monad[ListWrapper] = ListWrapper.monad + implicit val FS: Profunctor[IndexedStateT[ListWrapper, ?, ?, Int]] = IndexedStateT.catsDataProfunctorForIndexedStateT - checkAll("StateT[ListWrapper, Int, Int]", FunctorTests[StateT[ListWrapper, Int, ?]].functor[Int, Int, Int]) - checkAll("Functor[StateT[ListWrapper, Int, ?]]", SerializableTests.serializable(Functor[StateT[ListWrapper, Int, ?]])) + checkAll("IndexedStateT[ListWrapper, ?, Int, Int]", ProfunctorTests[IndexedStateT[ListWrapper, ?, ?, Int]].profunctor[Int, Int, Int, Int, Int, Int]) + checkAll("Profunctor[IndexedStateT[ListWrapper, ?, Int, Int]]", SerializableTests.serializable(Profunctor[IndexedStateT[ListWrapper, ?, ?, Int]])) - Functor[StateT[ListWrapper, Int, ?]] + Profunctor[IndexedStateT[ListWrapper, ?, ?, Int]] } { // F has a Monad implicit val F = ListWrapper.monad - checkAll("StateT[ListWrapper, Int, Int]", MonadTests[StateT[ListWrapper, Int, ?]].monad[Int, Int, Int]) - checkAll("Monad[StateT[ListWrapper, Int, ?]]", SerializableTests.serializable(Monad[StateT[ListWrapper, Int, ?]])) + checkAll("IndexedStateT[ListWrapper, Int, Int]", MonadTests[IndexedStateT[ListWrapper, Int, Int, ?]].monad[Int, Int, Int]) + checkAll("Monad[StateT[ListWrapper, Int, ?]]", SerializableTests.serializable(Monad[IndexedStateT[ListWrapper, Int, Int, ?]])) - Monad[StateT[ListWrapper, Int, ?]] - FlatMap[StateT[ListWrapper, Int, ?]] - Applicative[StateT[ListWrapper, Int, ?]] - Apply[StateT[ListWrapper, Int, ?]] - Functor[StateT[ListWrapper, Int, ?]] + Monad[IndexedStateT[ListWrapper, Int, Int, ?]] + FlatMap[IndexedStateT[ListWrapper, Int, Int, ?]] + Applicative[IndexedStateT[ListWrapper, Int, Int, ?]] + Apply[IndexedStateT[ListWrapper, Int, Int, ?]] + Functor[IndexedStateT[ListWrapper, Int, Int, ?]] } { @@ -238,26 +287,26 @@ class StateTTests extends CatsSuite { implicit val F = ListWrapper.monad implicit val S = ListWrapper.semigroupK - checkAll("StateT[ListWrapper, Int, Int]", SemigroupKTests[StateT[ListWrapper, Int, ?]].semigroupK[Int]) - checkAll("SemigroupK[StateT[ListWrapper, Int, ?]]", SerializableTests.serializable(SemigroupK[StateT[ListWrapper, Int, ?]])) + checkAll("IndexedStateT[ListWrapper, Int, Int]", SemigroupKTests[IndexedStateT[ListWrapper, Int, Int, ?]].semigroupK[Int]) + checkAll("SemigroupK[IndexedStateT[ListWrapper, Int, ?]]", SerializableTests.serializable(SemigroupK[IndexedStateT[ListWrapper, String, Int, ?]])) } { // F has an Alternative implicit val G = ListWrapper.monad implicit val F = ListWrapper.alternative - val SA = StateT.catsDataAlternativeForStateT[ListWrapper, Int](ListWrapper.monad, ListWrapper.alternative) - checkAll("StateT[ListWrapper, Int, Int]", AlternativeTests[StateT[ListWrapper, Int, ?]](SA).monoidK[Int]) - checkAll("Alternative[StateT[ListWrapper, Int, ?]]", SerializableTests.serializable(SA)) + val SA = IndexedStateT.catsDataAlternativeForIndexedStateT[ListWrapper, Int](ListWrapper.monad, ListWrapper.alternative) + checkAll("IndexedStateT[ListWrapper, Int, Int, Int]", AlternativeTests[IndexedStateT[ListWrapper, Int, Int, ?]](SA).monoidK[Int]) + checkAll("Alternative[IndexedStateT[ListWrapper, Int, Int, ?]]", SerializableTests.serializable(SA)) - Monad[StateT[ListWrapper, Int, ?]] - FlatMap[StateT[ListWrapper, Int, ?]] - Alternative[StateT[ListWrapper, Int, ?]] - Applicative[StateT[ListWrapper, Int, ?]] - Apply[StateT[ListWrapper, Int, ?]] - Functor[StateT[ListWrapper, Int, ?]] - MonoidK[StateT[ListWrapper, Int, ?]] - SemigroupK[StateT[ListWrapper, Int, ?]] + Monad[IndexedStateT[ListWrapper, Int, Int, ?]] + FlatMap[IndexedStateT[ListWrapper, Int, Int, ?]] + Alternative[IndexedStateT[ListWrapper, Int, Int, ?]] + Applicative[IndexedStateT[ListWrapper, Int, Int, ?]] + Apply[IndexedStateT[ListWrapper, Int, Int, ?]] + Functor[IndexedStateT[ListWrapper, Int, Int, ?]] + MonoidK[IndexedStateT[ListWrapper, Int, Int, ?]] + SemigroupK[IndexedStateT[ListWrapper, Int, Int, ?]] } { @@ -278,16 +327,16 @@ class StateTTests extends CatsSuite { } -object StateTTests extends StateTTestsInstances { +object IndexedStateTTests extends IndexedStateTTestsInstances { implicit def stateEq[S:Eq:Arbitrary, A:Eq]: Eq[State[S, A]] = - stateTEq[Eval, S, A] + indexedStateTEq[Eval, S, S, A] val add1: State[Int, Int] = State(n => (n + 1, n)) } -sealed trait StateTTestsInstances { +sealed trait IndexedStateTTestsInstances { - implicit def stateTEq[F[_], S, A](implicit S: Arbitrary[S], FSA: Eq[F[(S, A)]], F: FlatMap[F]): Eq[StateT[F, S, A]] = - Eq.by[StateT[F, S, A], S => F[(S, A)]](state => + implicit def indexedStateTEq[F[_], SA, SB, A](implicit SA: Arbitrary[SA], FSB: Eq[F[(SB, A)]], F: FlatMap[F]): Eq[IndexedStateT[F, SA, SB, A]] = + Eq.by[IndexedStateT[F, SA, SB, A], SA => F[(SB, A)]](state => s => state.run(s)) }