From ff44c4d7650e0fcda377341a0e950409325118c3 Mon Sep 17 00:00:00 2001 From: Itamar Ravid <iravid@bigpanda.io> Date: Sat, 5 Aug 2017 23:54:25 +0300 Subject: [PATCH] Convert ReaderWriterStateT to IndexedReaderWriterStateT Resolves #1774. --- .../scala/cats/data/ReaderWriterStateT.scala | 318 +++++++++++------- core/src/main/scala/cats/data/package.scala | 12 +- .../cats/laws/discipline/Arbitrary.scala | 5 +- .../cats/tests/ReaderWriterStateTTests.scala | 105 ++++-- 4 files changed, 271 insertions(+), 169 deletions(-) diff --git a/core/src/main/scala/cats/data/ReaderWriterStateT.scala b/core/src/main/scala/cats/data/ReaderWriterStateT.scala index fdd352bfb82..a0fe2d9bd93 100644 --- a/core/src/main/scala/cats/data/ReaderWriterStateT.scala +++ b/core/src/main/scala/cats/data/ReaderWriterStateT.scala @@ -5,55 +5,71 @@ import cats.functor.{ Contravariant, Profunctor } import cats.syntax.either._ /** - * Represents a stateful computation in a context `F[_]`, over state `S`, with an initial environment `E`, - * an accumulated log `L` and a result `A`. + * Represents a stateful computation in a context `F[_]`, from state `SA` to state `SB`, + * with an initial environment `E`, an accumulated log `L` and a result `A`. * * In other words, it is a pre-baked stack of `[[ReaderT]][F, E, A]`, `[[WriterT]][F, L, A]` - * and `[[StateT]][F, S, A]`. + * and `[[IndexedStateT]][F, SA, SB, A]`. */ -final class ReaderWriterStateT[F[_], E, L, S, A](val runF: F[(E, S) => F[(L, S, A)]]) extends Serializable { +final class IndexedReaderWriterStateT[F[_], E, L, SA, SB, A](val runF: F[(E, SA) => F[(L, SB, A)]]) extends Serializable { /** - * Modify the initial environment using `f`. + * Modify the initial state using `f`. */ - def contramap[E0](f: E0 => E)(implicit F: Functor[F]): ReaderWriterStateT[F, E0, L, S, A] = - ReaderWriterStateT.applyF { - F.map(runF) { rwsa => - (e0: E0, s: S) => rwsa(f(e0), s) + def contramap[S0](f: S0 => SA)(implicit F: Functor[F]): IndexedReaderWriterStateT[F, E, L, S0, SB, A] = + IndexedReaderWriterStateT.applyF { + F.map(runF) { rwsfa => + (e: E, s0: S0) => rwsfa(e, f(s0)) } } /** - * Alias for [[contramap]]. - */ - def local[EE](f: EE => E)(implicit F: Functor[F]): ReaderWriterStateT[F, EE, L, S, A] = - contramap(f) + * Modify the initial environment using `f`. + * + * {{{ + * scala> import cats.implicits._ + * scala> type Env = String + * scala> type GlobalEnv = (Int, Env) + * scala> type Log = List[String] + * scala> val xLocal: IndexedReaderWriterStateT[Option, Env, Log, Int, Int, Int] = IndexedReaderWriterStateT.get + * scala> val xGlobal: IndexedReaderWriterStateT[Option, GlobalEnv, Log, Int, Int, Int] = xLocal.local(_._2) + * }}} + */ + def local[EE](f: EE => E)(implicit F: Functor[F]): IndexedReaderWriterStateT[F, EE, L, SA, SB, A] = + IndexedReaderWriterStateT.applyF { + F.map(runF) { rwsa => + (ee: EE, sa: SA) => rwsa(f(ee), sa) + } + } /** * Modify the result of the computation using `f`. */ - def map[B](f: A => B)(implicit F: Functor[F]): ReaderWriterStateT[F, E, L, S, B] = + def map[B](f: A => B)(implicit F: Functor[F]): IndexedReaderWriterStateT[F, E, L, SA, SB, B] = transform { (l, s, a) => (l, s, f(a)) } + def dimap[S0, S1](f: S0 => SA)(g: SB => S1)(implicit F: Functor[F]): IndexedReaderWriterStateT[F, E, L, S0, S1, A] = + contramap(f).modify(g) + /** * Modify the written log value using `f`. */ - def mapWritten[LL](f: L => LL)(implicit F: Functor[F]): ReaderWriterStateT[F, E, LL, S, A] = + def mapWritten[LL](f: L => LL)(implicit F: Functor[F]): IndexedReaderWriterStateT[F, E, LL, SA, SB, A] = transform { (l, s, a) => (f(l), s, a) } /** * Modify the result of the computation by feeding it into `f`, threading the state * through the resulting computation and combining the log values. */ - def flatMap[B](f: A => ReaderWriterStateT[F, E, L, S, B])( - implicit F: FlatMap[F], L: Semigroup[L]): ReaderWriterStateT[F, E, L, S, B] = - ReaderWriterStateT.applyF { + def flatMap[SC, B](f: A => IndexedReaderWriterStateT[F, E, L, SB, SC, B])( + implicit F: FlatMap[F], L: Semigroup[L]): IndexedReaderWriterStateT[F, E, L, SA, SC, B] = + IndexedReaderWriterStateT.applyF { F.map(runF) { rwsfa => - (e: E, s0: S) => - F.flatMap(rwsfa(e, s0)) { case (la, sa, a) => + (e: E, sa: SA) => + F.flatMap(rwsfa(e, sa)) { case (la, sb, a) => F.flatMap(f(a).runF) { rwsfb => - F.map(rwsfb(e, sa)) { case (lb, sb, b) => - (L.combine(la, lb), sb, b) + F.map(rwsfb(e, sb)) { case (lb, sc, b) => + (L.combine(la, lb), sc, b) } } } @@ -63,12 +79,12 @@ final class ReaderWriterStateT[F[_], E, L, S, A](val runF: F[(E, S) => F[(L, S, /** * Like [[map]], but allows the mapping function to return an effectful value. */ - def flatMapF[B](faf: A => F[B])(implicit F: FlatMap[F]): ReaderWriterStateT[F, E, L, S, B] = - ReaderWriterStateT.applyF { + def flatMapF[B](faf: A => F[B])(implicit F: FlatMap[F]): IndexedReaderWriterStateT[F, E, L, SA, SB, B] = + IndexedReaderWriterStateT.applyF { F.map(runF) { rwsfa => - (e: E, s: S) => - F.flatMap(rwsfa(e, s)) { case (l, s, a) => - F.map(faf(a))((l, s, _)) + (e: E, sa: SA) => + F.flatMap(rwsfa(e, sa)) { case (l, sb, a) => + F.map(faf(a))((l, sb, _)) } } } @@ -76,193 +92,235 @@ final class ReaderWriterStateT[F[_], E, L, S, A](val runF: F[(E, S) => F[(L, S, /** * Transform the resulting log, state and value using `f`. */ - def transform[LL, B](f: (L, S, A) => (LL, S, B))(implicit F: Functor[F]): ReaderWriterStateT[F, E, LL, S, B] = - ReaderWriterStateT.applyF { + def transform[LL, SC, B](f: (L, SB, A) => (LL, SC, B))(implicit F: Functor[F]): IndexedReaderWriterStateT[F, E, LL, SA, SC, B] = + IndexedReaderWriterStateT.applyF { F.map(runF) { rwsfa => - (e: E, s: S) => F.map(rwsfa(e, s)) { case (l, s, a) => - val (ll, sb, b) = f(l, s, a) - (ll, sb, b) + (e: E, s: SA) => F.map(rwsfa(e, s)) { case (l, sb, a) => + val (ll, sc, b) = f(l, sb, a) + (ll, sc, b) } } } /** * Like [[transform]], but allows the context to change from `F` to `G`. - */ - def transformF[G[_], LL, B](f: F[(L, S, A)] => G[(LL, S, B)])( - implicit F: Monad[F], G: Applicative[G]): ReaderWriterStateT[G, E, LL, S, B] = - ReaderWriterStateT.apply((e, s) => f(run(e, s))) + * + * {{{ + * scala> import cats.implicits._ + * scala> type ErrorOr[A] = Either[String, A] + * scala> type Env = String + * scala> type Log = List[String] + * scala> val xError: IndexedReaderWriterStateT[ErrorOr, Env, Log, Int, Int, Int] = IndexedReaderWriterStateT.get + * scala> val xOpt: IndexedReaderWriterStateT[Option, Env, Log, Int, Int, Int] = xError.transformF(_.toOption) + * scala> val input = 5 + * scala> xError.run("env", input) + * res0: ErrorOr[(Log, Int, Int)] = Right((List(),5,5)) + * scala> xOpt.run("env", 5) + * res1: Option[(Log, Int, Int)] = Some((List(),5,5)) + * }}} + */ + def transformF[G[_], LL, SC, B](f: F[(L, SB, A)] => G[(LL, SC, B)])( + implicit F: Monad[F], G: Applicative[G]): IndexedReaderWriterStateT[G, E, LL, SA, SC, B] = + IndexedReaderWriterStateT.apply((e, s) => f(run(e, s))) /** * Modify the resulting state. */ - def modify(f: S => S)(implicit F: Functor[F]): ReaderWriterStateT[F, E, L, S, A] = - transform { (l, s, a) => (l, f(s), a) } + def modify[SC](f: SB => SC)(implicit F: Functor[F]): IndexedReaderWriterStateT[F, E, L, SA, SC, A] = + transform { (l, sb, a) => (l, f(sb), a) } /** * Inspect a value from the input state, without modifying the state. */ - def inspect[B](f: S => B)(implicit F: Functor[F]): ReaderWriterStateT[F, E, L, S, B] = - transform { (l, s, a) => (l, s, f(s)) } + def inspect[B](f: SB => B)(implicit F: Functor[F]): IndexedReaderWriterStateT[F, E, L, SA, SB, B] = + transform { (l, sb, a) => (l, sb, f(sb)) } /** * Get the input state, without modifying it. */ - def get(implicit F: Functor[F]): ReaderWriterStateT[F, E, L, S, S] = + def get(implicit F: Functor[F]): IndexedReaderWriterStateT[F, E, L, SA, SB, SB] = inspect(identity) /** * Add a value to the log. */ - def tell(l: L)(implicit F: Functor[F], L: Semigroup[L]): ReaderWriterStateT[F, E, L, S, A] = + def tell(l: L)(implicit F: Functor[F], L: Semigroup[L]): IndexedReaderWriterStateT[F, E, L, SA, SB, A] = mapWritten(L.combine(_, l)) /** * Retrieve the value written to the log. */ - def written(implicit F: Functor[F]): ReaderWriterStateT[F, E, L, S, L] = - transform { (l, s, a) => (l, s, l) } + def written(implicit F: Functor[F]): IndexedReaderWriterStateT[F, E, L, SA, SB, L] = + transform { (l, sb, a) => (l, sb, l) } /** * Clear the log. */ - def reset(implicit F: Functor[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, A] = + def reset(implicit F: Functor[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, SA, SB, A] = mapWritten(_ => L.empty) /** * Run the computation using the provided initial environment and state. */ - def run(env: E, initial: S)(implicit F: Monad[F]): F[(L, S, A)] = + def run(env: E, initial: SA)(implicit F: Monad[F]): F[(L, SB, A)] = F.flatMap(runF)(_.apply(env, initial)) /** * Run the computation using the provided environment and an empty state. */ - def runEmpty(env: E)(implicit F: Monad[F], S: Monoid[S]): F[(L, S, A)] = - run(env, S.empty) + def runEmpty(env: E)(implicit F: Monad[F], SA: Monoid[SA]): F[(L, SB, A)] = + run(env, SA.empty) /** * Like [[run]], but discards the final state and log. */ - def runA(env: E, initial: S)(implicit F: Monad[F]): F[A] = + def runA(env: E, initial: SA)(implicit F: Monad[F]): F[A] = F.map(run(env, initial))(_._3) /** * Like [[run]], but discards the final value and log. */ - def runS(env: E, initial: S)(implicit F: Monad[F]): F[S] = + def runS(env: E, initial: SA)(implicit F: Monad[F]): F[SB] = F.map(run(env, initial))(_._2) /** * Like [[run]], but discards the final state and value. */ - def runL(env: E, initial: S)(implicit F: Monad[F]): F[L] = + def runL(env: E, initial: SA)(implicit F: Monad[F]): F[L] = F.map(run(env, initial))(_._1) /** * Like [[runEmpty]], but discards the final state and log. */ - def runEmptyA(env: E)(implicit F: Monad[F], S: Monoid[S]): F[A] = - runA(env, S.empty) + def runEmptyA(env: E)(implicit F: Monad[F], SA: Monoid[SA]): F[A] = + runA(env, SA.empty) /** * Like [[runEmpty]], but discards the final value and log. */ - def runEmptyS(env: E)(implicit F: Monad[F], S: Monoid[S]): F[S] = - runS(env, S.empty) + def runEmptyS(env: E)(implicit F: Monad[F], SA: Monoid[SA]): F[SB] = + runS(env, SA.empty) /** * Like [[runEmpty]], but discards the final state and value. */ - def runEmptyL(env: E)(implicit F: Monad[F], S: Monoid[S]): F[L] = - runL(env, S.empty) + def runEmptyL(env: E)(implicit F: Monad[F], SA: Monoid[SA]): F[L] = + runL(env, SA.empty) } -object ReaderWriterStateT extends RWSTInstances { +private[data] sealed trait CommonIRWSTConstructors { /** - * Construct a new computation using the provided function. + * Return `a` and an empty log without modifying the input state. */ - def apply[F[_], E, L, S, A](runF: (E, S) => F[(L, S, A)])(implicit F: Applicative[F]): ReaderWriterStateT[F, E, L, S, A] = - new ReaderWriterStateT(F.pure(runF)) + def pure[F[_], E, L, S, A](a: A)(implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, S, S, A] = + IndexedReaderWriterStateT((_, s) => F.pure((L.empty, s, a))) /** - * Like [[apply]], but using a function in a context `F`. + * Return an effectful `a` and an empty log without modifying the input state. */ - def applyF[F[_], E, L, S, A](runF: F[(E, S) => F[(L, S, A)]]): ReaderWriterStateT[F, E, L, S, A] = - new ReaderWriterStateT(runF) + def lift[F[_], E, L, S, A](fa: F[A])(implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, S, S, A] = + IndexedReaderWriterStateT((_, s) => F.map(fa)((L.empty, s, _))) /** - * Return `a` and an empty log without modifying the input state. + * Inspect a value from the input state, without modifying the state. */ - def pure[F[_], E, L, S, A](a: A)(implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, A] = - ReaderWriterStateT((_, s) => F.pure((L.empty, s, a))) + def inspect[F[_], E, L, S, A](f: S => A)(implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, S, S, A] = + IndexedReaderWriterStateT((_, s) => F.pure((L.empty, s, f(s)))) /** - * Return an effectful `a` and an empty log without modifying the input state. + * Like [[inspect]], but using an effectful function. */ - def lift[F[_], E, L, S, A](fa: F[A])(implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, A] = - ReaderWriterStateT((_, s) => F.map(fa)((L.empty, s, _))) + def inspectF[F[_], E, L, S, A](f: S => F[A])(implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, S, S, A] = + IndexedReaderWriterStateT((_, s) => F.map(f(s))((L.empty, s, _))) /** - * Inspect a value from the input state, without modifying the state. + * Set the state to `s`. */ - def inspect[F[_], E, L, S, A](f: S => A)(implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, A] = - ReaderWriterStateT((_, s) => F.pure((L.empty, s, f(s)))) + def set[F[_], E, L, S](s: S)(implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, S, S, Unit] = + IndexedReaderWriterStateT((_, _) => F.pure((L.empty, s, ()))) /** - * Like [[inspect]], but using an effectful function. + * Like [[set]], but using an effectful `S` value. */ - def inspectF[F[_], E, L, S, A](f: S => F[A])(implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, A] = - ReaderWriterStateT((_, s) => F.map(f(s))((L.empty, s, _))) + def setF[F[_], E, L, S](fs: F[S])(implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, S, S, Unit] = + IndexedReaderWriterStateT((_, _) => F.map(fs)((L.empty, _, ()))) /** - * Modify the input state using `f`. + * Get the provided environment, without modifying the input state. */ - def modify[F[_], E, L, S](f: S => S)(implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, Unit] = - ReaderWriterStateT((_, s) => F.pure((L.empty, f(s), ()))) + def ask[F[_], E, L, S](implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, S, S, E] = + IndexedReaderWriterStateT((e, s) => F.pure((L.empty, s, e))) /** - * Like [[modify]], but using an effectful function. + * Add a value to the log, without modifying the input state. */ - def modifyF[F[_], E, L, S](f: S => F[S])(implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, Unit] = - ReaderWriterStateT((_, s) => F.map(f(s))((L.empty, _, ()))) + def tell[F[_], E, L, S](l: L)(implicit F: Applicative[F]): IndexedReaderWriterStateT[F, E, L, S, S, Unit] = + IndexedReaderWriterStateT((_, s) => F.pure((l, s, ()))) + + /** + * Like [[tell]], but using an effectful `L` value. + */ + def tellF[F[_], E, L, S](fl: F[L])(implicit F: Applicative[F]): IndexedReaderWriterStateT[F, E, L, S, S, Unit] = + IndexedReaderWriterStateT((_, s) => F.map(fl)((_, s, ()))) /** * Return the input state without modifying it. */ - def get[F[_], E, L, S](implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, S] = - ReaderWriterStateT((_, s) => F.pure((L.empty, s, s))) + def get[F[_], E, L, S](implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, S, S, S] = + IndexedReaderWriterStateT((_, s) => F.pure((L.empty, s, s))) +} +object IndexedReaderWriterStateT extends IRWSTInstances with CommonIRWSTConstructors { /** - * Set the state to `s`. + * Construct a new computation using the provided function. */ - def set[F[_], E, L, S](s: S)(implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, Unit] = - ReaderWriterStateT((_, _) => F.pure((L.empty, s, ()))) + def apply[F[_], E, L, SA, SB, A](runF: (E, SA) => F[(L, SB, A)])(implicit F: Applicative[F]): IndexedReaderWriterStateT[F, E, L, SA, SB, A] = + new IndexedReaderWriterStateT(F.pure(runF)) /** - * Like [[set]], but using an effectful `S` value. + * Like [[apply]], but using a function in a context `F`. */ - def setF[F[_], E, L, S](fs: F[S])(implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, Unit] = - ReaderWriterStateT((_, _) => F.map(fs)((L.empty, _, ()))) + def applyF[F[_], E, L, SA, SB, A](runF: F[(E, SA) => F[(L, SB, A)]]): IndexedReaderWriterStateT[F, E, L, SA, SB, A] = + new IndexedReaderWriterStateT(runF) /** - * Get the provided environment, without modifying the input state. + * Modify the input state using `f`. */ - def ask[F[_], E, L, S](implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, E] = - ReaderWriterStateT((e, s) => F.pure((L.empty, s, e))) + def modify[F[_], E, L, SA, SB](f: SA => SB)(implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, SA, SB, Unit] = + IndexedReaderWriterStateT((_, s) => F.pure((L.empty, f(s), ()))) /** - * Add a value to the log, without modifying the input state. + * Like [[modify]], but using an effectful function. */ - def tell[F[_], E, L, S](l: L)(implicit F: Applicative[F]): ReaderWriterStateT[F, E, L, S, Unit] = - ReaderWriterStateT((_, s) => F.pure((l, s, ()))) + def modifyF[F[_], E, L, SA, SB](f: SA => F[SB])(implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, SA, SB, Unit] = + IndexedReaderWriterStateT((_, s) => F.map(f(s))((L.empty, _, ()))) +} +private[data] abstract class RWSTFunctions extends CommonIRWSTConstructors { /** - * Like [[tell]], but using an effectful `L` value. + * Construct a new computation using the provided function. + */ + def apply[F[_], E, L, S, A](runF: (E, S) => F[(L, S, A)])(implicit F: Applicative[F]): ReaderWriterStateT[F, E, L, S, A] = + new IndexedReaderWriterStateT(F.pure(runF)) + + /** + * Like [[apply]], but using a function in a context `F`. */ - def tellF[F[_], E, L, S](fl: F[L])(implicit F: Applicative[F]): ReaderWriterStateT[F, E, L, S, Unit] = - ReaderWriterStateT((_, s) => F.map(fl)((_, s, ()))) + def applyF[F[_], E, L, S, A](runF: F[(E, S) => F[(L, S, A)]]): ReaderWriterStateT[F, E, L, S, A] = + new IndexedReaderWriterStateT(runF) + + /** + * Modify the input state using `f`. + */ + def modify[F[_], E, L, S](f: S => S)(implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, Unit] = + ReaderWriterStateT((_, s) => F.pure((L.empty, f(s), ()))) + + /** + * Like [[modify]], but using an effectful function. + */ + def modifyF[F[_], E, L, S](f: S => F[S])(implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, L, S, Unit] = + ReaderWriterStateT((_, s) => F.map(f(s))((L.empty, _, ()))) } /** @@ -318,18 +376,18 @@ private[data] abstract class RWSFunctions { ReaderWriterStateT.tell(l) } -private[data] sealed trait RWSTInstances extends RWSTInstances1 { - implicit def catsDataProfunctorForRWST[F[_], L, S](implicit F0: Functor[F]): Profunctor[ReaderWriterStateT[F, ?, L, S, ?]] = - new RWSTProfunctor[F, L, S] { +private[data] sealed trait IRWSTInstances extends IRWSTInstances1 { + implicit def catsDataProfunctorForRWST[F[_], E, L, T](implicit F0: Functor[F]): Profunctor[IndexedReaderWriterStateT[F, E, L, ?, ?, T]] = + new IRWSTProfunctor[F, E, L, T] { implicit def F: Functor[F] = F0 } - implicit def catsDataContravariantForRWST[F[_], L, S, A](implicit F0: Functor[F]): Contravariant[ReaderWriterStateT[F, ?, L, S, A]] = - new RWSTContravariant[F, L, S, A] { + implicit def catsDataContravariantForIRWST[F[_], E, L, SB, T](implicit F0: Functor[F]): Contravariant[IndexedReaderWriterStateT[F, E, L, ?, SB, T]] = + new IRWSTContravariant[F, E, L, SB, T] { implicit def F: Functor[F] = F0 } - implicit def catsDataMonadErrorForRWST[F[_], E, L, S, R](implicit F0: MonadError[F, R], L0: Monoid[L]): MonadError[ReaderWriterStateT[F, E, L, S, ?], R] = + implicit def catsDataMonadErrorForIRWST[F[_], E, L, S, R](implicit F0: MonadError[F, R], L0: Monoid[L]): MonadError[IndexedReaderWriterStateT[F, E, L, S, S, ?], R] = new RWSTMonadError[F, E, L, S, R] { implicit def F: MonadError[F, R] = F0 implicit def L: Monoid[L] = L0 @@ -337,7 +395,7 @@ private[data] sealed trait RWSTInstances extends RWSTInstances1 { } -private[data] sealed trait RWSTInstances1 extends RWSTInstances2 { +private[data] sealed trait IRWSTInstances1 extends IRWSTInstances2 { implicit def catsDataMonadForRWST[F[_], E, L, S](implicit F0: Monad[F], L0: Monoid[L]): Monad[ReaderWriterStateT[F, E, L, S, ?]] = new RWSTMonad[F, E, L, S] { implicit def F: Monad[F] = F0 @@ -345,9 +403,9 @@ private[data] sealed trait RWSTInstances1 extends RWSTInstances2 { } } -private[data] sealed trait RWSTInstances2 extends RWSTInstances3 { - implicit def catsDataAlternativeForRWST[F[_], E, L, S]( - implicit FM: Monad[F], FA: Alternative[F], L0: Monoid[L]): Alternative[ReaderWriterStateT[F, E, L, S, ?]] = +private[data] sealed trait IRWSTInstances2 extends IRWSTInstances3 { + implicit def catsDataAlternativeForIRWST[F[_], E, L, S](implicit FM: Monad[F], FA: Alternative[F], + L0: Monoid[L]): Alternative[IndexedReaderWriterStateT[F, E, L, S, S, ?]] = new RWSTAlternative[F, E, L, S] { implicit def G: Alternative[F] = FA implicit def F: Monad[F] = FM @@ -355,41 +413,42 @@ private[data] sealed trait RWSTInstances2 extends RWSTInstances3 { } } -private[data] sealed trait RWSTInstances3 { - implicit def catsDataSemigroupKForRWST[F[_], E, L, S]( - implicit F0: Monad[F], G0: SemigroupK[F]): SemigroupK[ReaderWriterStateT[F, E, L, S, ?]] = - new RWSTSemigroupK[F, E, L, S] { +private[data] sealed trait IRWSTInstances3 { + implicit def catsDataSemigroupKForIRWST[F[_], E, L, SA, SB](implicit F0: Monad[F], + G0: SemigroupK[F]): SemigroupK[IndexedReaderWriterStateT[F, E, L, SA, SB, ?]] = + new IRWSTSemigroupK[F, E, L, SA, SB] { implicit def F: Monad[F] = F0 implicit def G: SemigroupK[F] = G0 } - implicit def catsDataFunctorForRWST[F[_], E, L, S](implicit F0: Functor[F]): Functor[ReaderWriterStateT[F, E, L, S, ?]] = - new RWSTFunctor[F, E, L, S] { + + implicit def catsDataFunctorForIRWST[F[_], E, L, SA, SB](implicit F0: Functor[F]): Functor[IndexedReaderWriterStateT[F, E, L, SA, SB, ?]] = + new IRWSTFunctor[F, E, L, SA, SB] { implicit def F: Functor[F] = F0 } } -private[data] sealed trait RWSTFunctor[F[_], E, L, S] extends Functor[ReaderWriterStateT[F, E, L, S, ?]] { +private[data] sealed trait IRWSTFunctor[F[_], E, L, SA, SB] extends Functor[IndexedReaderWriterStateT[F, E, L, SA, SB, ?]] { implicit def F: Functor[F] - override def map[A, B](fa: ReaderWriterStateT[F, E, L, S, A])(f: A => B): ReaderWriterStateT[F, E, L, S, B] = + override def map[A, B](fa: IndexedReaderWriterStateT[F, E, L, SA, SB, A])(f: A => B): IndexedReaderWriterStateT[F, E, L, SA, SB, B] = fa.map(f) } -private[data] sealed trait RWSTContravariant[F[_], L, S, T] extends Contravariant[ReaderWriterStateT[F, ?, L, S, T]] { +private[data] sealed trait IRWSTContravariant[F[_], E, L, SB, T] extends Contravariant[IndexedReaderWriterStateT[F, E, L, ?, SB, T]] { implicit def F: Functor[F] - override def contramap[A, B](fa: ReaderWriterStateT[F, A, L, S, T])(f: B => A): ReaderWriterStateT[F, B, L, S, T] = + override def contramap[A, B](fa: IndexedReaderWriterStateT[F, E, L, A, SB, T])(f: B => A): IndexedReaderWriterStateT[F, E, L, B, SB, T] = fa.contramap(f) } -private[data] sealed trait RWSTProfunctor[F[_], L, S] extends Profunctor[ReaderWriterStateT[F, ?, L, S, ?]] { +private[data] sealed trait IRWSTProfunctor[F[_], E, L, T] extends Profunctor[IndexedReaderWriterStateT[F, E, L, ?, ?, T]] { implicit def F: Functor[F] - override def dimap[A, B, C, D](fab: ReaderWriterStateT[F, A, L, S, B])(f: C => A)(g: B => D): ReaderWriterStateT[F, C, L, S, D] = - fab.contramap(f).map(g) + override def dimap[A, B, C, D](fab: IndexedReaderWriterStateT[F, E, L, A, B, T])(f: C => A)(g: B => D): IndexedReaderWriterStateT[F, E, L, C, D, T] = + fab.dimap(f)(g) } -private[data] sealed trait RWSTMonad[F[_], E, L, S] extends Monad[ReaderWriterStateT[F, E, L, S, ?]] with RWSTFunctor[F, E, L, S] { +private[data] sealed trait RWSTMonad[F[_], E, L, S] extends Monad[ReaderWriterStateT[F, E, L, S, ?]] with IRWSTFunctor[F, E, L, S, S] { implicit def F: Monad[F] implicit def L: Monoid[L] @@ -409,19 +468,20 @@ private[data] sealed trait RWSTMonad[F[_], E, L, S] extends Monad[ReaderWriterSt } } -private[data] sealed trait RWSTSemigroupK[F[_], E, L, S] extends SemigroupK[ReaderWriterStateT[F, E, L, S, ?]] { +private[data] sealed trait IRWSTSemigroupK[F[_], E, L, SA, SB] extends SemigroupK[IndexedReaderWriterStateT[F, E, L, SA, SB, ?]] { implicit def F: Monad[F] implicit def G: SemigroupK[F] - def combineK[A](x: ReaderWriterStateT[F, E, L, S, A], y: ReaderWriterStateT[F, E, L, S, A]): ReaderWriterStateT[F, E, L, S, A] = - ReaderWriterStateT { (e, s) => - G.combineK(x.run(e, s), y.run(e, s)) + def combineK[A](x: IndexedReaderWriterStateT[F, E, L, SA, SB, A], + y: IndexedReaderWriterStateT[F, E, L, SA, SB, A]): IndexedReaderWriterStateT[F, E, L, SA, SB, A] = + IndexedReaderWriterStateT { (e, sa) => + G.combineK(x.run(e, sa), y.run(e, sa)) } } private[data] sealed trait RWSTAlternative[F[_], E, L, S] - extends Alternative[ReaderWriterStateT[F, E, L, S, ?]] with RWSTFunctor[F, E, L, S] - with RWSTSemigroupK[F, E, L, S] { + extends Alternative[ReaderWriterStateT[F, E, L, S, ?]] with IRWSTFunctor[F, E, L, S, S] + with IRWSTSemigroupK[F, E, L, S, S] { implicit def F: Monad[F] override def G: Alternative[F] diff --git a/core/src/main/scala/cats/data/package.scala b/core/src/main/scala/cats/data/package.scala index e367b1599ca..60092158e27 100644 --- a/core/src/main/scala/cats/data/package.scala +++ b/core/src/main/scala/cats/data/package.scala @@ -32,7 +32,17 @@ package object data { type State[S, A] = StateT[Eval, S, A] object State extends StateFunctions - type RWST[F[_], E, S, L, A] = ReaderWriterStateT[F, E, S, L, A] + type IRWST[F[_], E, L, SA, SB, A] = IndexedReaderWriterStateT[F, E, L, SA, SB, A] + val IRWST = IndexedReaderWriterStateT + + /** + * Represents a stateful computation in a context `F[_]`, over state `S`, with an + * initial environment `E`, an accumulated log `L` and a result `A`. + */ + type ReaderWriterStateT[F[_], E, L, S, A] = IndexedReaderWriterStateT[F, E, L, S, S, A] + object ReaderWriterStateT extends RWSTFunctions + + type RWST[F[_], E, L, S, A] = ReaderWriterStateT[F, E, L, S, A] val RWST = ReaderWriterStateT type ReaderWriterState[E, L, S, A] = ReaderWriterStateT[Eval, E, L, S, 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..e5e181ba03c 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -161,8 +161,9 @@ object arbitrary extends ArbitraryInstances0 { implicit def catsLawArbitraryForCokleisliId[A: Arbitrary: Cogen, B: Arbitrary]: Arbitrary[Cokleisli[Id, A, B]] = catsLawsArbitraryForCokleisli[Id, A, B] - implicit def catsLawsArbitraryForReaderWriterStateT[F[_]: Applicative, E, L, S, A](implicit F: Arbitrary[(E, S) => F[(L, S, A)]]): Arbitrary[ReaderWriterStateT[F, E, L, S, A]] = - Arbitrary(F.arbitrary.map(ReaderWriterStateT(_))) + implicit def catsLawsArbitraryForIRWST[F[_]: Applicative, E, L, SA, SB, A](implicit + F: Arbitrary[(E, SA) => F[(L, SB, A)]]): Arbitrary[IndexedReaderWriterStateT[F, E, L, SA, SB, A]] = + Arbitrary(F.arbitrary.map(IndexedReaderWriterStateT(_))) } diff --git a/tests/src/test/scala/cats/tests/ReaderWriterStateTTests.scala b/tests/src/test/scala/cats/tests/ReaderWriterStateTTests.scala index de379464a18..77652b1fb61 100644 --- a/tests/src/test/scala/cats/tests/ReaderWriterStateTTests.scala +++ b/tests/src/test/scala/cats/tests/ReaderWriterStateTTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.data.{ ReaderWriterStateT, ReaderWriterState, EitherT } +import cats.data.{ IRWST, IndexedReaderWriterStateT, ReaderWriterStateT, ReaderWriterState, EitherT } import cats.functor.{ Contravariant, Profunctor } import cats.laws.discipline._ import cats.laws.discipline.eq._ @@ -46,21 +46,14 @@ class ReaderWriterStateTTests extends CatsSuite { } } - test("local is consistent with contramap") { - forAll { (context: Int, initial: Int, f: Int => String) => - val rwsa = ReaderWriterState.pure[String, Unit, Int, Unit](()).contramap(f).flatMap(_ => ReaderWriterState.ask) - val rwsb = ReaderWriterState.pure[String, Unit, Int, Unit](()).local(f).flatMap(_ => ReaderWriterState.ask) - - rwsa.runA(context, initial) should === (rwsb.runA(context, initial)) - } - } - - test("ReaderWriterState.pure and ReaderWriterStateT.pure are consistent") { + test("ReaderWriterState.pure, ReaderWriterStateT.pure and IndexedReaderWriterStateT.pure are consistent") { forAll { (value: Int) => val rws: ReaderWriterState[String, Vector[String], Int, Int] = ReaderWriterState.pure(value) val rwst: ReaderWriterState[String, Vector[String], Int, Int] = ReaderWriterStateT.pure(value) + val irwst: ReaderWriterState[String, Vector[String], Int, Int] = IndexedReaderWriterStateT.pure(value) rws should === (rwst) + rwst should === (irwst) } } @@ -71,16 +64,27 @@ class ReaderWriterStateTTests extends CatsSuite { } } - test("ReaderWriterState.get and ReaderWriterStateT.get are consistent") { + test("ReaderWriterState.get, ReaderWriterStateT.get and IndexedReaderWriterStateT.get are consistent") { forAll { (initial: Int) => val rws: ReaderWriterState[String, Vector[String], Int, Int] = ReaderWriterState.get val rwst: ReaderWriterState[String, Vector[String], Int, Int] = ReaderWriterStateT.get + val irwst: ReaderWriterState[String, Vector[String], Int, Int] = IndexedReaderWriterStateT.get rws should === (rwst) + rwst should === (irwst) } } test("ReaderWriterState.get and instance get are consistent") { + forAll { (initial: Int) => + val singleton = ReaderWriterState.get[String, String, Int] + val instance = ReaderWriterState.pure[String, String, Int, Unit](()).get + + singleton should === (instance) + } + } + + test("ReaderWriterState.inspect and instance inspect are consistent") { forAll { (initial: Int) => val singleton = ReaderWriterState.inspect[String, String, Int, String](_.toString) val instance = ReaderWriterState.pure[String, String, Int, Unit](()).inspect(_.toString) @@ -89,84 +93,102 @@ class ReaderWriterStateTTests extends CatsSuite { } } - test("ReaderWriterState.inspect and ReaderWriterStateT.inspect are consistent") { + test("ReaderWriterState.inspect, ReaderWriterStateT.inspect and IndexedReaderWriterStateT.inspect are consistent") { forAll { (f: Int => Int) => val rws: ReaderWriterState[String, Vector[String], Int, Int] = ReaderWriterState.inspect(f) val rwst: ReaderWriterState[String, Vector[String], Int, Int] = ReaderWriterStateT.inspect(f) + val irwst: ReaderWriterState[String, Vector[String], Int, Int] = IndexedReaderWriterStateT.inspect(f) rws should === (rwst) + rwst should === (irwst) } } - test("ReaderWriterState.inspect and ReaderWriterStateT.inspectF are consistent") { + test("ReaderWriterState.inspect, ReaderWriterStateT.inspectF and IndexedReaderWriterStateT.inspectF are consistent") { forAll { (f: Int => Int) => val rws: ReaderWriterState[String, String, Int, Int] = ReaderWriterState.inspect(f) val rwst: ReaderWriterState[String, String, Int, Int] = ReaderWriterStateT.inspectF(f.andThen(Eval.now)) + val irwst: ReaderWriterState[String, String, Int, Int] = IndexedReaderWriterStateT.inspectF(f.andThen(Eval.now)) rws should === (rwst) + rwst should === (irwst) } } - test("ReaderWriterState.modify and ReaderWriterStateT.modify are consistent") { + test("ReaderWriterState.modify, ReaderWriterStateT.modify and IndexedReaderWriterStateT.modify are consistent") { forAll { (f: Int => Int) => val rws: ReaderWriterState[String, Vector[String], Int, Unit] = ReaderWriterState.modify(f) val rwst: ReaderWriterState[String, Vector[String], Int, Unit] = ReaderWriterStateT.modify(f) + val irwst: ReaderWriterState[String, Vector[String], Int, Unit] = IndexedReaderWriterStateT.modify(f) rws should === (rwst) + rwst should === (irwst) } } - test("ReaderWriterState.modify and ReaderWriterStateT.modifyF are consistent") { + test("ReaderWriterState.modify, ReaderWriterStateT.modifyF and IndexedReaderWriterStateT.modifyF are consistent") { forAll { (f: Int => Int) => val rws: ReaderWriterState[String, Vector[String], Int, Unit] = ReaderWriterState.modify(f) val rwst: ReaderWriterState[String, Vector[String], Int, Unit] = ReaderWriterStateT.modifyF(f.andThen(Eval.now)) + val irwst: ReaderWriterState[String, Vector[String], Int, Unit] = IndexedReaderWriterStateT.modifyF(f.andThen(Eval.now)) rws should === (rwst) + rwst should === (irwst) } } - test("ReaderWriterState.pure and ReaderWriterStateT.lift are consistent") { + test("ReaderWriterState.pure, ReaderWriterStateT.lift and IndexedReaderWriterStateT.lift are consistent") { forAll { (value: Int) => val rws: ReaderWriterState[String, Vector[String], Int, Int] = ReaderWriterState.pure(value) val rwst: ReaderWriterState[String, Vector[String], Int, Int] = ReaderWriterStateT.lift(Eval.now(value)) + val irwst: ReaderWriterState[String, Vector[String], Int, Int] = IndexedReaderWriterStateT.lift(Eval.now(value)) rws should === (rwst) + rwst should === (irwst) } } - test("ReaderWriterState.set and ReaderWriterStateT.set are consistent") { + test("ReaderWriterState.set, ReaderWriterStateT.set and IndexedReaderWriterStateT.set are consistent") { forAll { (next: Int) => val rws: ReaderWriterState[String, Vector[String], Int, Unit] = ReaderWriterState.set(next) val rwst: ReaderWriterState[String, Vector[String], Int, Unit] = ReaderWriterStateT.set(next) + val irwst: ReaderWriterState[String, Vector[String], Int, Unit] = IndexedReaderWriterStateT.set(next) rws should === (rwst) + rwst should === (irwst) } } - test("ReaderWriterState.set and ReaderWriterStateT.setF are consistent") { + test("ReaderWriterState.set, ReaderWriterStateT.setF and IndexedReaderWriterStateT.setF are consistent") { forAll { (next: Int) => val rws: ReaderWriterState[String, Vector[String], Int, Unit] = ReaderWriterState.set(next) val rwst: ReaderWriterState[String, Vector[String], Int, Unit] = ReaderWriterStateT.setF(Eval.now(next)) + val irwst: ReaderWriterState[String, Vector[String], Int, Unit] = IndexedReaderWriterStateT.setF(Eval.now(next)) rws should === (rwst) + rwst should === (irwst) } } - test("ReaderWriterState.tell and ReaderWriterStateT.tell are consistent") { + test("ReaderWriterState.tell, ReaderWriterStateT.tell and IndexedReaderWriterStateT.tell are consistent") { forAll { (log: String) => val rws: ReaderWriterState[String, String, Int, Unit] = ReaderWriterState.tell(log) val rwst: ReaderWriterState[String, String, Int, Unit] = ReaderWriterStateT.tell(log) + val irwst: ReaderWriterState[String, String, Int, Unit] = IndexedReaderWriterStateT.tell(log) rws should === (rwst) + rwst should === (irwst) } } - test("ReaderWriterState.tell and ReaderWriterStateT.tellF are consistent") { + test("ReaderWriterState.tell, ReaderWriterStateT.tellF and IndexedReaderWriterStateT.tellF are consistent") { forAll { (log: String) => val rws: ReaderWriterState[String, String, Int, Unit] = ReaderWriterState.tell(log) val rwst: ReaderWriterState[String, String, Int, Unit] = ReaderWriterStateT.tellF(Eval.now(log)) + val irwst: ReaderWriterState[String, String, Int, Unit] = IndexedReaderWriterStateT.tellF(Eval.now(log)) rws should === (rwst) + rwst should === (irwst) } } @@ -280,26 +302,35 @@ class ReaderWriterStateTTests extends CatsSuite { } implicit val iso = CartesianTests.Isomorphisms - .invariant[ReaderWriterStateT[ListWrapper, String, String, Int, ?]](ReaderWriterStateT.catsDataFunctorForRWST(ListWrapper.functor)) + .invariant[IndexedReaderWriterStateT[ListWrapper, String, String, Int, String, ?]](IndexedReaderWriterStateT.catsDataFunctorForIRWST(ListWrapper.functor)) { implicit val F: Monad[ListWrapper] = ListWrapper.monad - checkAll("ReaderWriterStateT[ListWrapper, String, String, Int, Int]", - FunctorTests[ReaderWriterStateT[ListWrapper, String, String, Int, ?]].functor[Int, Int, Int]) - checkAll("Functor[ReaderWriterStateT[ListWrapper, String, String, Int, ?]]", - SerializableTests.serializable(Functor[ReaderWriterStateT[ListWrapper, String, String, Int, ?]])) + checkAll("IndexedReaderWriterStateT[ListWrapper, String, String, Int, String, Int]", + FunctorTests[IndexedReaderWriterStateT[ListWrapper, String, String, Int, String, ?]].functor[Int, Int, Int]) + checkAll("Functor[IndexedReaderWriterStateT[ListWrapper, String, String, Int, String, ?]]", + SerializableTests.serializable(Functor[IndexedReaderWriterStateT[ListWrapper, String, String, Int, String, ?]])) - checkAll("ReaderWriterStateT[ListWrapper, String, String, Int, Int]", - ContravariantTests[ReaderWriterStateT[ListWrapper, ?, String, Int, Int]].contravariant[String, String, String]) - checkAll("Contravariant[ReaderWriterStateT[ListWrapper, ?, String, Int, Int]]", - SerializableTests.serializable(Contravariant[ReaderWriterStateT[ListWrapper, ?, String, Int, Int]])) + checkAll("IndexedReaderWriterStateT[ListWrapper, String, String, String, Int, Int]", + ContravariantTests[IndexedReaderWriterStateT[ListWrapper, String, String, ?, Int, Int]].contravariant[String, String, String]) + checkAll("Contravariant[IndexedReaderWriterStateT[ListWrapper, String, String, ?, Int, Int]]", + SerializableTests.serializable(Contravariant[IndexedReaderWriterStateT[ListWrapper, String, String, ?, Int, Int]])) + + checkAll("IndexedReaderWriterStateT[ListWrapper, String, String, Int, String, Int]", + ProfunctorTests[IndexedReaderWriterStateT[ListWrapper, String, String, ?, ?, Int]].profunctor[Int, Int, Int, String, String, String]) + checkAll("Profunctor[ReaderWriterStateT[ListWrapper, String, String, ?, ?, Int]]", + SerializableTests.serializable(Profunctor[IndexedReaderWriterStateT[ListWrapper, String, String, ?, ?, Int]])) + + } + + { + implicit val G: Monad[ListWrapper] = ListWrapper.monad - checkAll("ReaderWriterStateT[ListWrapper, Int, String, Int, Int]", - ProfunctorTests[ReaderWriterStateT[ListWrapper, ?, String, Int, ?]].profunctor[Int, Int, Int, Int, Int, Int]) - checkAll("Profunctor[ReaderWriterStateT[ListWrapper, ?, String, Int, ?]]", - SerializableTests.serializable(Profunctor[ReaderWriterStateT[ListWrapper, ?, String, Int, ?]])) + val SA = IRWST.catsDataAlternativeForIRWST[ListWrapper, String, String, Int](ListWrapper.monad, ListWrapper.alternative, Monoid[String]) + checkAll("IndexedReaderWriterStateT[ListWrapper, String, String, Int, Int, Int]", AlternativeTests[IRWST[ListWrapper, String, String, Int, Int, ?]](SA).monoidK[Int]) + checkAll("Alternative[IndexedReaderWriterStateT[ListWrapper, String, String, Int, Int, ?]]", SerializableTests.serializable(SA)) } { @@ -349,9 +380,9 @@ object ReaderWriterStateTTests { ReaderWriterState { (context, state) => ((), state + i, state + i) } } - implicit def RWSTEq[F[_], E, L, S, A](implicit S: Arbitrary[S], E: Arbitrary[E], FLSA: Eq[F[(L, S, A)]], - F: Monad[F]): Eq[ReaderWriterStateT[F, E, L, S, A]] = - Eq.by[ReaderWriterStateT[F, E, L, S, A], (E, S) => F[(L, S, A)]] { state => + implicit def IRWSTEq[F[_], E, L, SA, SB, A](implicit SA: Arbitrary[SA], SB: Arbitrary[SB], E: Arbitrary[E], + FLSB: Eq[F[(L, SB, A)]], F: Monad[F]): Eq[IndexedReaderWriterStateT[F, E, L, SA, SB, A]] = + Eq.by[IndexedReaderWriterStateT[F, E, L, SA, SB, A], (E, SA) => F[(L, SB, A)]] { state => (e, s) => state.run(e, s) } }