Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MonadError instance for StateT. #1413

Merged
merged 1 commit into from
Oct 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions core/src/main/scala/cats/data/StateT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,19 @@ private[data] sealed trait StateTInstances1 extends StateTInstances2 {
new StateTMonadCombine[F, S] { implicit def F = F0 }
}

private[data] sealed trait StateTInstances2 {
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 StateTInstances2 extends StateTInstances3 {
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 }

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

private[data] sealed trait StateTInstances3 {
implicit def catsDataMonadForStateT[F[_], S](implicit F0: Monad[F]): Monad[StateT[F, S, ?]] =
new StateTMonad[F, S] { implicit def F = F0 }
}

// To workaround SI-7139 `object State` needs to be defined inside the package object
// together with the type alias.
private[data] abstract class StateFunctions {
Expand Down Expand Up @@ -258,3 +263,12 @@ private[data] sealed trait StateTMonadCombine[F[_], S] extends MonadCombine[Stat

def empty[A]: StateT[F, S, A] = liftT[F, A](F.empty[A])
}

private[data] sealed trait StateTMonadError[F[_], S, E] extends StateTMonad[F, S] with MonadError[StateT[F, S, ?], E] {
implicit def F: MonadError[F, E]

def raiseError[A](e: E): StateT[F, S, A] = StateT.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)))
}
12 changes: 11 additions & 1 deletion tests/src/test/scala/cats/tests/StateTTests.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats
package tests

import cats.data.{State, StateT}
import cats.data.{State, StateT, EitherT}
import cats.kernel.instances.tuple._
import cats.laws.discipline._
import cats.laws.discipline.eq._
Expand Down Expand Up @@ -260,6 +260,16 @@ class StateTTests extends CatsSuite {
checkAll("State[Long, ?]", MonadTests[State[Long, ?]].monad[Int, Int, Int])
checkAll("Monad[State[Long, ?]]", SerializableTests.serializable(Monad[State[Long, ?]]))
}

{
// F has a MonadError
implicit val iso = CartesianTests.Isomorphisms.invariant[StateT[Option, Int, ?]]
implicit val eqEitherTFA: Eq[EitherT[StateT[Option, Int , ?], Unit, Int]] = EitherT.catsDataEqForEitherT[StateT[Option, Int , ?], Unit, Int]

checkAll("StateT[Option, Int, Int]", MonadErrorTests[StateT[Option, Int , ?], Unit].monadError[Int, Int, Int])
checkAll("MonadError[StateT[Option, Int , ?], Unit]", SerializableTests.serializable(MonadError[StateT[Option, Int , ?], Unit]))
}

}

object StateTTests extends StateTTestsInstances {
Expand Down