From 289c538456018994ffb9ddd738000f5d4514e885 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Sat, 29 Jul 2017 22:53:44 +0300 Subject: [PATCH 01/16] 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 | 162 +++++++++---- 6 files changed, 280 insertions(+), 128 deletions(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 82c5d21f3e..fe90559e1b 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 e367b1599c..0e95a297ba 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 a5f2f30f1e..51329cb8f8 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 963d71faa2..4e719bd641 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 063f389f9a..1a1c0eae4e 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 81c1ee99ed..7c9d86d90a 100644 --- a/tests/src/test/scala/cats/tests/StateTTests.scala +++ b/tests/src/test/scala/cats/tests/StateTTests.scala @@ -1,99 +1,143 @@ 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) x.runS(0).value should === (70001) } - test("State.pure and StateT.pure are consistent"){ + test("State.pure, StateT.pure and IndexedStateT.pure are consistent"){ forAll { (s: String, i: Int) => val state: State[String, Int] = State.pure(i) val stateT: State[String, Int] = StateT.pure(i) + val indexedStateT: State[String, Int] = IndexedStateT.pure(i) + state.run(s) should === (stateT.run(s)) + state.run(s) should === (indexedStateT.run(s)) } } - test("State.get and StateT.get are consistent") { + test("State.get, StateT.get and IndexedStateT.get are consistent") { forAll{ (s: String) => val state: State[String, String] = State.get val stateT: State[String, String] = StateT.get + val indexedStateT: State[String, String] = IndexedStateT.get + state.run(s) should === (stateT.run(s)) + state.run(s) should === (indexedStateT.run(s)) } } - test("State.inspect and StateT.inspect are consistent") { + test("State.inspect, StateT.inspect and IndexedStateT.inspect are consistent") { forAll { (s: String, f: String => Int) => val state: State[String, Int] = State.inspect(f) val stateT: State[String, Int] = StateT.inspect(f) + val indexedStateT: State[String, Int] = IndexedStateT.inspect(f) + state.run(s) should === (stateT.run(s)) + state.run(s) should === (indexedStateT.run(s)) } } - test("State.inspect and StateT.inspectF are consistent") { + test("State.inspect, StateT.inspectF and IndexedStateT.inspectF are consistent") { forAll { (s: String, f: String => Int) => val state: State[String, Int] = State.inspect(f) val stateT: State[String, Int] = StateT.inspectF(f.andThen(Eval.now)) + val indexedStateT: State[String, Int] = IndexedStateT.inspectF(f.andThen(Eval.now)) + state.run(s) should === (stateT.run(s)) + state.run(s) should === (indexedStateT.run(s)) } } - test("State.modify and StateT.modify are consistent") { + test("State.modify, StateT.modify and IndexedStateT.modify are consistent") { forAll { (s: String, f: String => String) => val state: State[String, Unit] = State.modify(f) val stateT: State[String, Unit] = StateT.modify(f) + val indexedStateT: State[String, Unit] = IndexedStateT.modify(f) + state.run(s) should === (stateT.run(s)) + state.run(s) should === (indexedStateT.run(s)) } } - test("State.modify and StateT.modifyF are consistent") { + test("State.modify, StateT.modifyF and IndexedStateT.modifyF are consistent") { forAll { (s: String, f: String => String) => val state: State[String, Unit] = State.modify(f) val stateT: State[String, Unit] = StateT.modifyF(f.andThen(Eval.now)) + val indexedStateT: State[String, Unit] = IndexedStateT.modifyF(f.andThen(Eval.now)) + state.run(s) should === (stateT.run(s)) + state.run(s) should === (indexedStateT.run(s)) } } - test("State.pure and StateT.lift are consistent") { + test("State.pure, StateT.lift and IndexedStateT.lift are consistent") { forAll { (s: String, i: Int) => val state: State[String, Int] = State.pure(i) val stateT: State[String, Int] = StateT.lift(Eval.now(i)) + val indexedStateT: State[String, Int] = IndexedStateT.lift(Eval.now(i)) + state.run(s) should === (stateT.run(s)) + state.run(s) should === (indexedStateT.run(s)) } } - test("State.set and StateT.set are consistent") { + test("State.set, StateT.set and IndexedStateT.set are consistent") { forAll { (init: String, s: String) => val state: State[String, Unit] = State.set(s) val stateT: StateT[Eval, String, Unit] = StateT.set(s) + val indexedStateT: StateT[Eval, String, Unit] = IndexedStateT.set(s) + state.run(init) should === (stateT.run(init)) + state.run(init) should === (indexedStateT.run(init)) } } - test("State.set and StateT.setF are consistent") { + test("State.set, StateT.setF and IndexedStateT.setF are consistent") { forAll { (init: String, s: String) => val state: State[String, Unit] = State.set(s) val stateT: StateT[Eval, String, Unit] = StateT.setF(Eval.now(s)) + val indexedStateT: StateT[Eval, String, Unit] = IndexedStateT.setF(Eval.now(s)) + state.run(init) should === (stateT.run(init)) + state.run(init) should === (indexedStateT.run(init)) } } @@ -199,38 +243,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 +314,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 +354,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)) } From 9ffcbdb3b9fdbec778a2eb61eda580d4c40c1763 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Fri, 4 Aug 2017 11:56:37 +0300 Subject: [PATCH 02/16] Add doctest for transformF --- core/src/main/scala/cats/data/StateT.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index fe90559e1b..02aeaed043 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -93,6 +93,18 @@ final class IndexedStateT[F[_], SA, SB, A](val runF: F[SA => F[(SB, A)]]) extend /** * 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))) From b2b00a76b3aac75a1c102bb1484d8556ce557219 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Fri, 4 Aug 2017 12:00:55 +0300 Subject: [PATCH 03/16] Add law checking for applicative --- tests/src/test/scala/cats/tests/StateTTests.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/src/test/scala/cats/tests/StateTTests.scala b/tests/src/test/scala/cats/tests/StateTTests.scala index 7c9d86d90a..ffddd4822e 100644 --- a/tests/src/test/scala/cats/tests/StateTTests.scala +++ b/tests/src/test/scala/cats/tests/StateTTests.scala @@ -323,7 +323,9 @@ class IndexedStateTTests extends CatsSuite { implicit val G = ListWrapper.monad implicit val F = ListWrapper.alternative 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("IndexedStateT[ListWrapper, Int, Int, Int]", ApplicativeTests[IndexedStateT[ListWrapper, Int, Int, ?]](SA).applicative[Int, Int, Int]) checkAll("Alternative[IndexedStateT[ListWrapper, Int, Int, ?]]", SerializableTests.serializable(SA)) Monad[IndexedStateT[ListWrapper, Int, Int, ?]] From f51ba694b01a107ae9081ccdbe9f928dbf2bc20f Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Fri, 4 Aug 2017 12:06:39 +0300 Subject: [PATCH 04/16] Move common StateT constructors to separate trait --- core/src/main/scala/cats/data/StateT.scala | 39 ++++++++-------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 02aeaed043..0a728d4a08 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -155,13 +155,7 @@ final class IndexedStateT[F[_], SA, SB, A](val runF: F[SA => F[(SB, A)]]) extend inspect(identity) } -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) - +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))) @@ -174,15 +168,23 @@ object IndexedStateT extends IndexedStateTInstances { 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 CommonStateTConstructors with 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 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, ()))) @@ -190,34 +192,19 @@ object IndexedStateT extends IndexedStateTInstances { IndexedStateT(_ => F.map(fsb)(s => (s, ()))) } -private[data] abstract class StateTFunctions { +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 pure[F[_], S, A](a: A)(implicit F: Applicative[F]): StateT[F, S, A] = - apply(s => F.pure((s, a))) - - def lift[F[_], S, A](fa: F[A])(implicit F: Applicative[F]): StateT[F, 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] = - apply(s => F.pure((s, f(s)))) - - def inspectF[F[_], S, A](f: S => F[A])(implicit F: Applicative[F]): StateT[F, 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] = 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 get[F[_], S](implicit F: Applicative[F]): StateT[F, S, S] = - apply(s => F.pure((s, s))) - def set[F[_], S](s: S)(implicit F: Applicative[F]): StateT[F, S, Unit] = apply(_ => F.pure((s, ()))) From 84e1db04f74bd56e7eb498f00fe9a03f3f460cef Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Fri, 4 Aug 2017 12:30:01 +0300 Subject: [PATCH 05/16] Remove explicit `ap` override in StateT's Alternative instance --- core/src/main/scala/cats/data/StateT.scala | 21 ++++++++----------- .../test/scala/cats/tests/StateTTests.scala | 1 - 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 0a728d4a08..436f3133ea 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -214,7 +214,12 @@ private[data] abstract class StateTFunctions extends CommonStateTConstructors { 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 } + new IndexedStateTAlternative[F, S] with IndexedStateTMonad[F, S] { + implicit def F = FM + implicit def G = FA + + override def pure[A](a: A): IndexedStateT[F, S, S, A] = super[IndexedStateTMonad].pure(a) + } } private[data] sealed trait IndexedStateTInstances1 extends IndexedStateTInstances2 { @@ -299,13 +304,13 @@ private[data] sealed trait IndexedStateTMonad[F[_], S] extends Monad[IndexedStat with IndexedStateTFunctor[F, S, S] { implicit def F: Monad[F] - def pure[A](a: A): StateT[F, S, A] = + def pure[A](a: A): IndexedStateT[F, S, S, A] = IndexedStateT.pure(a) - def flatMap[A, B](fa: StateT[F, S, A])(f: A => StateT[F, S, B]): StateT[F, S, B] = + 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 => StateT[F, S, Either[A, B]]): StateT[F, S, B] = + 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, _)) } }) @@ -331,14 +336,6 @@ private[data] sealed trait IndexedStateTAlternative[F[_], S] extends Alternative def empty[A]: IndexedStateT[F, S, S, A] = IndexedStateT.lift[F, S, A](G.empty[A])(G) - - 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)) } - } - ) } private[data] sealed trait IndexedStateTMonadError[F[_], S, E] extends IndexedStateTMonad[F, S] diff --git a/tests/src/test/scala/cats/tests/StateTTests.scala b/tests/src/test/scala/cats/tests/StateTTests.scala index ffddd4822e..926da59ede 100644 --- a/tests/src/test/scala/cats/tests/StateTTests.scala +++ b/tests/src/test/scala/cats/tests/StateTTests.scala @@ -325,7 +325,6 @@ class IndexedStateTTests extends CatsSuite { 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("IndexedStateT[ListWrapper, Int, Int, Int]", ApplicativeTests[IndexedStateT[ListWrapper, Int, Int, ?]](SA).applicative[Int, Int, Int]) checkAll("Alternative[IndexedStateT[ListWrapper, Int, Int, ?]]", SerializableTests.serializable(SA)) Monad[IndexedStateT[ListWrapper, Int, Int, ?]] From a005a61247cbcd5267ec6d25f4563fe1b9355f2c Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Fri, 4 Aug 2017 12:31:44 +0300 Subject: [PATCH 06/16] Add previous `StateT` documentation to type alias --- core/src/main/scala/cats/data/package.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/scala/cats/data/package.scala b/core/src/main/scala/cats/data/package.scala index 0e95a297ba..0d82370fb3 100644 --- a/core/src/main/scala/cats/data/package.scala +++ b/core/src/main/scala/cats/data/package.scala @@ -29,6 +29,12 @@ package object data { def tell[L](l: L): Writer[L, Unit] = WriterT.tell(l) } + /** + * `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. + */ type StateT[F[_], S, A] = IndexedStateT[F, S, S, A] object StateT extends StateTFunctions From 342319941d8d34bb59bf8d634320c3728211bbb1 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Sat, 5 Aug 2017 21:32:36 +0300 Subject: [PATCH 07/16] Make IndexedStateTAlternative extend IndexedStateTMonad --- core/src/main/scala/cats/data/StateT.scala | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 436f3133ea..d9b9be9cf2 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -214,12 +214,7 @@ private[data] abstract class StateTFunctions extends CommonStateTConstructors { 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] with IndexedStateTMonad[F, S] { - implicit def F = FM - implicit def G = FA - - override def pure[A](a: A): IndexedStateT[F, S, S, A] = super[IndexedStateTMonad].pure(a) - } + new IndexedStateTAlternative[F, S] { implicit def F = FM; implicit def G = FA } } private[data] sealed trait IndexedStateTInstances1 extends IndexedStateTInstances2 { @@ -324,16 +319,12 @@ private[data] sealed trait IndexedStateTSemigroupK[F[_], SA, SB] extends Semigro IndexedStateT(s => G.combineK(x.run(s), y.run(s))) } -private[data] sealed trait IndexedStateTAlternative[F[_], S] extends Alternative[IndexedStateT[F, S, S, ?]] with IndexedStateTFunctor[F, S, S] { - implicit def F: Monad[F] +private[data] sealed trait IndexedStateTAlternative[F[_], S] extends Alternative[IndexedStateT[F, S, S, ?]] with IndexedStateTMonad[F, 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 pure[A](a: A): IndexedStateT[F, S, S, A] = - IndexedStateT.pure[F, S, A](a)(G) - def empty[A]: IndexedStateT[F, S, S, A] = IndexedStateT.lift[F, S, A](G.empty[A])(G) } From 1315a290ffba042f8bf93aa9eae5a9b933cfc364 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Sat, 5 Aug 2017 21:54:34 +0300 Subject: [PATCH 08/16] Downgrade contramap and dimap to Applicative on F --- core/src/main/scala/cats/data/StateT.scala | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index d9b9be9cf2..a4775b4e77 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -35,14 +35,10 @@ final class IndexedStateT[F[_], SA, SB, A](val runF: F[SA => F[(SB, A)]]) extend 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 contramap[S0](f: S0 => SA)(implicit F: Applicative[F]): IndexedStateT[F, S0, SB, A] = + IndexedStateT.applyF(F.map2(F.pure(f), runF)(_ andThen _)) - def dimap[S0, S1](f: S0 => SA)(g: SB => S1)(implicit F: Monad[F]): IndexedStateT[F, S0, S1, A] = + def dimap[S0, S1](f: S0 => SA)(g: SB => S1)(implicit F: Applicative[F]): IndexedStateT[F, S0, S1, A] = contramap(f).modify(g) /** @@ -234,10 +230,10 @@ 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]] = + implicit def catsDataContravariantForIndexedStateT[F[_], SB, V](implicit F0: Applicative[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]] = + implicit def catsDataProfunctorForIndexedStateT[F[_], V](implicit F0: Applicative[F]): Profunctor[IndexedStateT[F, ?, ?, V]] = new IndexedStateTProfunctor[F, V] { implicit def F = F0 } } @@ -282,14 +278,14 @@ private[data] sealed trait IndexedStateTFunctor[F[_], SA, SB] extends Functor[In } private[data] sealed trait IndexedStateTContravariant[F[_], SB, V] extends Contravariant[IndexedStateT[F, ?, SB, V]] { - implicit def F: Monad[F] + implicit def F: Applicative[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 IndexedStateTProfunctor[F[_], V] extends Profunctor[IndexedStateT[F, ?, ?, V]] { - implicit def F: Monad[F] + implicit def F: Applicative[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) From 1d8419e0feb6d9bc229d3c9502c92fadefef5003 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Sun, 6 Aug 2017 00:00:07 +0300 Subject: [PATCH 09/16] Downgrade contramap and dimap to Functor on F --- core/src/main/scala/cats/data/StateT.scala | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index a4775b4e77..09f1032f5f 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -35,10 +35,14 @@ final class IndexedStateT[F[_], SA, SB, A](val runF: F[SA => F[(SB, A)]]) extend 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: Applicative[F]): IndexedStateT[F, S0, SB, A] = - IndexedStateT.applyF(F.map2(F.pure(f), runF)(_ andThen _)) + 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 dimap[S0, S1](f: S0 => SA)(g: SB => S1)(implicit F: Applicative[F]): IndexedStateT[F, S0, S1, A] = + def dimap[S0, S1](f: S0 => SA)(g: SB => S1)(implicit F: Functor[F]): IndexedStateT[F, S0, S1, A] = contramap(f).modify(g) /** @@ -230,10 +234,10 @@ 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: Applicative[F]): Contravariant[IndexedStateT[F, ?, SB, V]] = + 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: Applicative[F]): Profunctor[IndexedStateT[F, ?, ?, V]] = + implicit def catsDataProfunctorForIndexedStateT[F[_], V](implicit F0: Functor[F]): Profunctor[IndexedStateT[F, ?, ?, V]] = new IndexedStateTProfunctor[F, V] { implicit def F = F0 } } @@ -278,14 +282,14 @@ private[data] sealed trait IndexedStateTFunctor[F[_], SA, SB] extends Functor[In } private[data] sealed trait IndexedStateTContravariant[F[_], SB, V] extends Contravariant[IndexedStateT[F, ?, SB, V]] { - implicit def F: Applicative[F] + 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 trait IndexedStateTProfunctor[F[_], V] extends Profunctor[IndexedStateT[F, ?, ?, V]] { - implicit def F: Applicative[F] + 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) From 49de02f22c2e3d4e99439384a020ce82849f8c83 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Tue, 8 Aug 2017 23:05:47 +0300 Subject: [PATCH 10/16] Specify full intersection type for returned Alternative instance --- core/src/main/scala/cats/data/StateT.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 09f1032f5f..21c4169e88 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -213,7 +213,8 @@ private[data] abstract class StateTFunctions extends CommonStateTConstructors { } private[data] sealed trait IndexedStateTInstances extends IndexedStateTInstances1 { - implicit def catsDataAlternativeForIndexedStateT[F[_], S](implicit FM: Monad[F], FA: Alternative[F]): Alternative[StateT[F, S, ?]] = + 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 } } From 0268e47498a76fbc6ca3e5f90fc881776e96382b Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Wed, 9 Aug 2017 11:12:14 +0300 Subject: [PATCH 11/16] Add Bifunctor instance --- core/src/main/scala/cats/data/StateT.scala | 15 +++++++++- .../test/scala/cats/tests/StateTTests.scala | 30 ++++++++++++++----- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 21c4169e88..50bc1a7cee 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -1,7 +1,7 @@ package cats package data -import cats.functor.{ Contravariant, Profunctor } +import cats.functor.{ Contravariant, Bifunctor, Profunctor } import cats.syntax.either._ /** @@ -42,6 +42,9 @@ final class IndexedStateT[F[_], SA, SB, A](val runF: F[SA => F[(SB, A)]]) extend } } + 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) @@ -240,6 +243,9 @@ private[data] sealed trait IndexedStateTInstances3 { 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 } } // To workaround SI-7139 `object State` needs to be defined inside the package object @@ -289,6 +295,13 @@ private[data] sealed trait IndexedStateTContravariant[F[_], SB, V] extends Contr fa.contramap(f) } +private[data] sealed trait 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 trait IndexedStateTProfunctor[F[_], V] extends Profunctor[IndexedStateT[F, ?, ?, V]] { implicit def F: Functor[F] diff --git a/tests/src/test/scala/cats/tests/StateTTests.scala b/tests/src/test/scala/cats/tests/StateTTests.scala index 926da59ede..b0cb729c9b 100644 --- a/tests/src/test/scala/cats/tests/StateTTests.scala +++ b/tests/src/test/scala/cats/tests/StateTTests.scala @@ -2,7 +2,7 @@ package cats package tests import cats.data.{State, StateT, IndexedStateT, EitherT} -import cats.functor.{Contravariant, Profunctor} +import cats.functor.{Contravariant, Bifunctor, Profunctor} import cats.kernel.instances.tuple._ import cats.laws.discipline._ import cats.laws.discipline.eq._ @@ -253,14 +253,20 @@ class IndexedStateTTests extends CatsSuite { } { - // We only need a Monad to derive a Contravariant for IndexedStateT - implicit val F: Monad[ListWrapper] = ListWrapper.monad + // We only need a Functor to derive a Contravariant for IndexedStateT + implicit val F: Functor[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 + // We only need a Functor to derive a Bifunctor for IndexedStateT + implicit val F: Functor[ListWrapper] = ListWrapper.monad + Bifunctor[IndexedStateT[ListWrapper, Int, ?, ?]] + } + + { + // We only need a Functor to derive a Profunctor for IndexedStateT + implicit val F: Functor[ListWrapper] = ListWrapper.monad Profunctor[IndexedStateT[ListWrapper, ?, ?, String]] } @@ -279,17 +285,27 @@ class IndexedStateTTests extends CatsSuite { 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("IndexedStateT[ListWrapper, Int, 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: Bifunctor[IndexedStateT[ListWrapper, Int, ?, ?]] = IndexedStateT.catsDataBifunctorForIndexedStateT + + checkAll("IndexedStateT[ListWrapper, Int, String, Int]", BifunctorTests[IndexedStateT[ListWrapper, Int, ?, ?]].bifunctor[String, String, String, Int, Int, Int]) + checkAll("Bifunctor[IndexedStateT[ListWrapper, Int, ?, ?]]", SerializableTests.serializable(Bifunctor[IndexedStateT[ListWrapper, Int, ?, ?]])) + + Bifunctor[IndexedStateT[ListWrapper, Int, ?, ?]] + } + { implicit val F: Monad[ListWrapper] = ListWrapper.monad implicit val FS: Profunctor[IndexedStateT[ListWrapper, ?, ?, Int]] = IndexedStateT.catsDataProfunctorForIndexedStateT - checkAll("IndexedStateT[ListWrapper, ?, Int, Int]", ProfunctorTests[IndexedStateT[ListWrapper, ?, ?, Int]].profunctor[Int, Int, Int, Int, Int, Int]) + checkAll("IndexedStateT[ListWrapper, String, Int, Int]", ProfunctorTests[IndexedStateT[ListWrapper, ?, ?, Int]].profunctor[String, String, String, Int, Int, Int]) checkAll("Profunctor[IndexedStateT[ListWrapper, ?, Int, Int]]", SerializableTests.serializable(Profunctor[IndexedStateT[ListWrapper, ?, ?, Int]])) Profunctor[IndexedStateT[ListWrapper, ?, ?, Int]] From 7ee428a8d0e070a573edf050e1999c3137eb3816 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Fri, 1 Sep 2017 10:43:30 +0300 Subject: [PATCH 12/16] Convert instance traits to abstract classes --- core/src/main/scala/cats/data/StateT.scala | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 50bc1a7cee..a114fa5bb8 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -175,7 +175,7 @@ private[data] trait CommonStateTConstructors { IndexedStateT(s => F.pure((s, s))) } -object IndexedStateT extends CommonStateTConstructors with IndexedStateTInstances { +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)) @@ -215,13 +215,13 @@ private[data] abstract class StateTFunctions extends CommonStateTConstructors { apply(_ => F.map(fs)(s => (s, ()))) } -private[data] sealed trait IndexedStateTInstances extends IndexedStateTInstances1 { +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 trait IndexedStateTInstances1 extends IndexedStateTInstances2 { +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 } @@ -229,12 +229,12 @@ private[data] sealed trait IndexedStateTInstances1 extends IndexedStateTInstance new IndexedStateTSemigroupK[F, SA, SB] { implicit def F = F0; implicit def G = G0 } } -private[data] sealed trait IndexedStateTInstances2 extends IndexedStateTInstances3 { +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 trait IndexedStateTInstances3 { +private[data] sealed abstract class 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 } @@ -281,36 +281,35 @@ private[data] abstract class StateFunctions { def set[S](s: S): State[S, Unit] = State(_ => (s, ())) } -private[data] sealed trait IndexedStateTFunctor[F[_], SA, SB] extends Functor[IndexedStateT[F, SA, SB, ?]] { +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 trait IndexedStateTContravariant[F[_], SB, V] extends Contravariant[IndexedStateT[F, ?, SB, V]] { +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 trait IndexedStateTBifunctor[F[_], SA] extends Bifunctor[IndexedStateT[F, SA, ?, ?]] { +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 trait IndexedStateTProfunctor[F[_], V] extends Profunctor[IndexedStateT[F, ?, ?, V]] { +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 trait IndexedStateTMonad[F[_], S] extends Monad[IndexedStateT[F, S, S, ?]] - with IndexedStateTFunctor[F, S, S] { +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] = @@ -325,7 +324,7 @@ private[data] sealed trait IndexedStateTMonad[F[_], S] extends Monad[IndexedStat }) } -private[data] sealed trait IndexedStateTSemigroupK[F[_], SA, SB] extends SemigroupK[IndexedStateT[F, SA, SB, ?]] { +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] @@ -333,7 +332,7 @@ private[data] sealed trait IndexedStateTSemigroupK[F[_], SA, SB] extends Semigro IndexedStateT(s => G.combineK(x.run(s), y.run(s))) } -private[data] sealed trait IndexedStateTAlternative[F[_], S] extends Alternative[IndexedStateT[F, S, S, ?]] with IndexedStateTMonad[F, 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] = @@ -343,7 +342,7 @@ private[data] sealed trait IndexedStateTAlternative[F[_], S] extends Alternative IndexedStateT.lift[F, S, A](G.empty[A])(G) } -private[data] sealed trait IndexedStateTMonadError[F[_], S, E] extends IndexedStateTMonad[F, S] +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] From 3be4238203650588c6c5eb87850cf531dfcd44ae Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Sun, 10 Sep 2017 08:01:12 +0300 Subject: [PATCH 13/16] Refine comment on IndexedStateT --- core/src/main/scala/cats/data/StateT.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index a114fa5bb8..65c108a7a6 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -7,12 +7,15 @@ import cats.syntax.either._ /** * * `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 + * a value of type `A`. The 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 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. * - * Note that `IndexedStateT[F, SA, SB, A]` is not a monad, but an indexed monad. + * 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 { From 6e25fbb46ce1e4971ff548dc5c2a99f5ba9875d7 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Sun, 10 Sep 2017 10:18:19 +0300 Subject: [PATCH 14/16] Add Strong and Arrow instances for IndexedStateT --- core/src/main/scala/cats/data/StateT.scala | 51 ++++++++++++++++++- .../test/scala/cats/tests/StateTTests.scala | 25 ++++++++- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 65c108a7a6..aae1a48d60 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -1,7 +1,8 @@ package cats package data -import cats.functor.{ Contravariant, Bifunctor, Profunctor } +import cats.functor.{ Contravariant, Bifunctor, Profunctor, Strong } +import cats.arrow.Arrow import cats.syntax.either._ /** @@ -237,7 +238,7 @@ private[data] sealed abstract class IndexedStateTInstances2 extends IndexedState new IndexedStateTMonad[F, S] { implicit def F = F0 } } -private[data] sealed abstract class IndexedStateTInstances3 { +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 } @@ -251,6 +252,16 @@ private[data] sealed abstract class IndexedStateTInstances3 { new IndexedStateTBifunctor[F, SA] { implicit def F = F0 } } +private[data] sealed abstract class IndexedStateTInstances4 extends IndexedStateTInstances5 { + implicit def catsDataStrongForIndexedStateT[F[_], V](implicit F0: Monad[F]): Strong[IndexedStateT[F, ?, ?, V]] = + new IndexedStateTStrong[F, V] { implicit def F = F0 } +} + +private[data] sealed abstract class IndexedStateTInstances5 { + implicit def catsDataArrowForIndexedStateT[F[_], V](implicit F0: Monad[F], V0: Monoid[V]): Arrow[IndexedStateT[F, ?, ?, V]] = + new IndexedStateTArrow[F, V] { implicit def F = F0; implicit def V = V0 } +} + // To workaround SI-7139 `object State` needs to be defined inside the package object // together with the type alias. private[data] abstract class StateFunctions { @@ -312,6 +323,42 @@ private[data] sealed abstract class IndexedStateTProfunctor[F[_], V] extends Pro 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 IndexedStateTArrow[F[_], V] extends IndexedStateTStrong[F, V] with Arrow[IndexedStateT[F, ?, ?, V]] { + implicit def F: Monad[F] + implicit def V: Monoid[V] + + def lift[A, B](f: A => B): IndexedStateT[F, A, B, V] = + IndexedStateT { a => + F.pure((f(a), V.empty)) + } + + def id[A]: IndexedStateT[F, A, A, V] = + IndexedStateT.pure(V.empty) + + def compose[A, B, C](f: IndexedStateT[F, B, C, V], g: IndexedStateT[F, A, B, V]): IndexedStateT[F, A, C, V] = + IndexedStateT { a => + F.flatMap(g.run(a)) { case (b, va) => + F.map(f.run(b)) { case (c, vb) => + (c, V.combine(va, vb)) + } + } + } +} + private[data] sealed abstract class IndexedStateTMonad[F[_], S] extends IndexedStateTFunctor[F, S, S] with Monad[IndexedStateT[F, S, S, ?]] { implicit def F: Monad[F] diff --git a/tests/src/test/scala/cats/tests/StateTTests.scala b/tests/src/test/scala/cats/tests/StateTTests.scala index b0cb729c9b..37d4ce46ef 100644 --- a/tests/src/test/scala/cats/tests/StateTTests.scala +++ b/tests/src/test/scala/cats/tests/StateTTests.scala @@ -1,8 +1,9 @@ package cats package tests +import cats.arrow.Arrow import cats.data.{State, StateT, IndexedStateT, EitherT} -import cats.functor.{Contravariant, Bifunctor, Profunctor} +import cats.functor.{Contravariant, Bifunctor, Profunctor, Strong} import cats.kernel.instances.tuple._ import cats.laws.discipline._ import cats.laws.discipline.eq._ @@ -306,11 +307,31 @@ class IndexedStateTTests extends CatsSuite { implicit val FS: Profunctor[IndexedStateT[ListWrapper, ?, ?, Int]] = IndexedStateT.catsDataProfunctorForIndexedStateT checkAll("IndexedStateT[ListWrapper, String, Int, Int]", ProfunctorTests[IndexedStateT[ListWrapper, ?, ?, Int]].profunctor[String, String, String, Int, Int, Int]) - checkAll("Profunctor[IndexedStateT[ListWrapper, ?, Int, Int]]", SerializableTests.serializable(Profunctor[IndexedStateT[ListWrapper, ?, ?, Int]])) + checkAll("Profunctor[IndexedStateT[ListWrapper, ?, ?, Int]]", SerializableTests.serializable(Profunctor[IndexedStateT[ListWrapper, ?, ?, Int]])) Profunctor[IndexedStateT[ListWrapper, ?, ?, Int]] } + { + implicit val F: Monad[ListWrapper] = ListWrapper.monad + implicit val FS: Strong[IndexedStateT[ListWrapper, ?, ?, Int]] = IndexedStateT.catsDataStrongForIndexedStateT + + checkAll("IndexedStateT[ListWrapper, String, Int, Int]", StrongTests[IndexedStateT[ListWrapper, ?, ?, Int]].strong[String, String, String, Int, Int, Int]) + checkAll("Strong[IndexedStateT[ListWrapper, ?, ?, Int]]", SerializableTests.serializable(Strong[IndexedStateT[ListWrapper, ?, ?, Int]])) + + Strong[IndexedStateT[ListWrapper, ?, ?, Int]] + } + + { + implicit val F: Monad[ListWrapper] = ListWrapper.monad + implicit val FS: Arrow[IndexedStateT[ListWrapper, ?, ?, Int]] = IndexedStateT.catsDataArrowForIndexedStateT + + checkAll("IndexedStateT[ListWrapper, String, Int, Int]", ArrowTests[IndexedStateT[ListWrapper, ?, ?, Int]].arrow[String, String, String, Int, Int, Int]) + checkAll("Arrow[IndexedStateT[ListWrapper, ?, ?, Int]]", SerializableTests.serializable(Arrow[IndexedStateT[ListWrapper, ?, ?, Int]])) + + Arrow[IndexedStateT[ListWrapper, ?, ?, Int]] + } + { // F has a Monad implicit val F = ListWrapper.monad From 3151311227c16d5f967af3ccd942d6e251cd2750 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Sun, 10 Sep 2017 21:33:20 +0300 Subject: [PATCH 15/16] Rename files --- .../main/scala/cats/data/{StateT.scala => IndexedStateT.scala} | 0 .../cats/tests/{StateTTests.scala => IndexedStateTTests.scala} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename core/src/main/scala/cats/data/{StateT.scala => IndexedStateT.scala} (100%) rename tests/src/test/scala/cats/tests/{StateTTests.scala => IndexedStateTTests.scala} (100%) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/IndexedStateT.scala similarity index 100% rename from core/src/main/scala/cats/data/StateT.scala rename to core/src/main/scala/cats/data/IndexedStateT.scala diff --git a/tests/src/test/scala/cats/tests/StateTTests.scala b/tests/src/test/scala/cats/tests/IndexedStateTTests.scala similarity index 100% rename from tests/src/test/scala/cats/tests/StateTTests.scala rename to tests/src/test/scala/cats/tests/IndexedStateTTests.scala From 995d45181ef7f84c11eb8e75afd9711897d65c72 Mon Sep 17 00:00:00 2001 From: Itamar Ravid Date: Mon, 11 Sep 2017 08:48:44 +0300 Subject: [PATCH 16/16] Remove arrow instance --- .../main/scala/cats/data/IndexedStateT.scala | 30 +------------------ .../scala/cats/tests/IndexedStateTTests.scala | 11 ------- 2 files changed, 1 insertion(+), 40 deletions(-) diff --git a/core/src/main/scala/cats/data/IndexedStateT.scala b/core/src/main/scala/cats/data/IndexedStateT.scala index aae1a48d60..f2d855b07e 100644 --- a/core/src/main/scala/cats/data/IndexedStateT.scala +++ b/core/src/main/scala/cats/data/IndexedStateT.scala @@ -2,7 +2,6 @@ package cats package data import cats.functor.{ Contravariant, Bifunctor, Profunctor, Strong } -import cats.arrow.Arrow import cats.syntax.either._ /** @@ -252,16 +251,11 @@ private[data] sealed abstract class IndexedStateTInstances3 extends IndexedState new IndexedStateTBifunctor[F, SA] { implicit def F = F0 } } -private[data] sealed abstract class IndexedStateTInstances4 extends IndexedStateTInstances5 { +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 } } -private[data] sealed abstract class IndexedStateTInstances5 { - implicit def catsDataArrowForIndexedStateT[F[_], V](implicit F0: Monad[F], V0: Monoid[V]): Arrow[IndexedStateT[F, ?, ?, V]] = - new IndexedStateTArrow[F, V] { implicit def F = F0; implicit def V = V0 } -} - // To workaround SI-7139 `object State` needs to be defined inside the package object // together with the type alias. private[data] abstract class StateFunctions { @@ -337,28 +331,6 @@ private[data] sealed abstract class IndexedStateTStrong[F[_], V] extends Indexed first(fa).dimap((_: (C, A)).swap)(_.swap) } -private[data] sealed abstract class IndexedStateTArrow[F[_], V] extends IndexedStateTStrong[F, V] with Arrow[IndexedStateT[F, ?, ?, V]] { - implicit def F: Monad[F] - implicit def V: Monoid[V] - - def lift[A, B](f: A => B): IndexedStateT[F, A, B, V] = - IndexedStateT { a => - F.pure((f(a), V.empty)) - } - - def id[A]: IndexedStateT[F, A, A, V] = - IndexedStateT.pure(V.empty) - - def compose[A, B, C](f: IndexedStateT[F, B, C, V], g: IndexedStateT[F, A, B, V]): IndexedStateT[F, A, C, V] = - IndexedStateT { a => - F.flatMap(g.run(a)) { case (b, va) => - F.map(f.run(b)) { case (c, vb) => - (c, V.combine(va, vb)) - } - } - } -} - private[data] sealed abstract class IndexedStateTMonad[F[_], S] extends IndexedStateTFunctor[F, S, S] with Monad[IndexedStateT[F, S, S, ?]] { implicit def F: Monad[F] diff --git a/tests/src/test/scala/cats/tests/IndexedStateTTests.scala b/tests/src/test/scala/cats/tests/IndexedStateTTests.scala index 37d4ce46ef..cc77122c0f 100644 --- a/tests/src/test/scala/cats/tests/IndexedStateTTests.scala +++ b/tests/src/test/scala/cats/tests/IndexedStateTTests.scala @@ -1,7 +1,6 @@ package cats package tests -import cats.arrow.Arrow import cats.data.{State, StateT, IndexedStateT, EitherT} import cats.functor.{Contravariant, Bifunctor, Profunctor, Strong} import cats.kernel.instances.tuple._ @@ -322,16 +321,6 @@ class IndexedStateTTests extends CatsSuite { Strong[IndexedStateT[ListWrapper, ?, ?, Int]] } - { - implicit val F: Monad[ListWrapper] = ListWrapper.monad - implicit val FS: Arrow[IndexedStateT[ListWrapper, ?, ?, Int]] = IndexedStateT.catsDataArrowForIndexedStateT - - checkAll("IndexedStateT[ListWrapper, String, Int, Int]", ArrowTests[IndexedStateT[ListWrapper, ?, ?, Int]].arrow[String, String, String, Int, Int, Int]) - checkAll("Arrow[IndexedStateT[ListWrapper, ?, ?, Int]]", SerializableTests.serializable(Arrow[IndexedStateT[ListWrapper, ?, ?, Int]])) - - Arrow[IndexedStateT[ListWrapper, ?, ?, Int]] - } - { // F has a Monad implicit val F = ListWrapper.monad