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)
     }
 }