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.rethrow #2061

Merged
merged 8 commits into from
Dec 6, 2017
Merged
Show file tree
Hide file tree
Changes from 6 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
5 changes: 3 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,9 @@ def mimaSettings(moduleName: String) = Seq(
exclude[DirectMissingMethodProblem]("cats.data.ValidatedApplicative.sequence"),
exclude[ReversedMissingMethodProblem]("cats.data.IorFunctions.fromEither"),
exclude[DirectMissingMethodProblem]("cats.data.RWSTAlternative.traverse"),
exclude[DirectMissingMethodProblem]("cats.data.RWSTAlternative.sequence")

exclude[DirectMissingMethodProblem]("cats.data.RWSTAlternative.sequence"),
exclude[ReversedMissingMethodProblem]("cats.MonadError.rethrow"),
exclude[ReversedMissingMethodProblem]("cats.syntax.MonadErrorSyntax.catsSyntaxMonadErrorRethrow")
)
}
)
Expand Down
20 changes: 20 additions & 0 deletions core/src/main/scala/cats/MonadError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,26 @@ trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] {
*/
def adaptError[A](fa: F[A])(pf: PartialFunction[E, E]): F[A] =
flatMap(attempt(fa))(_.fold(e => raiseError(pf.applyOrElse[E, E](e, _ => e)), pure))

/**
* Inverse of `attempt`
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> import scala.util.{Try, Success}
*
* scala> val a: Try[Either[Throwable, Int]] = Success(Left(new Exception))
* scala> a.rethrow
* res0: scala.util.Try[Int] = Failure(java.lang.Exception)
*
* scala> val b: Try[Either[Throwable, Int]] = Success(Right(1))
* scala> b.rethrow
* res1: scala.util.Try[Int] = Success(1)
* }}}
*/
def rethrow[A](fa: F[Either[E, A]]): F[A] =
flatMap(fa)(_.fold(raiseError, pure))
}

object MonadError {
Expand Down
3 changes: 1 addition & 2 deletions core/src/main/scala/cats/data/Ior.scala
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,7 @@ private[data] sealed abstract class IorInstances extends IorInstances0 {
def handleErrorWith[B](fa: Ior[A, B])(f: (A) => Ior[A, B]): Ior[A, B] =
fa match {
case Ior.Left(e) => f(e)
case r @ Ior.Right(_) => r
case Ior.Both(e, _) => f(e)
case _ => fa
}

def flatMap[B, C](fa: Ior[A, B])(f: B => Ior[A, C]): Ior[A, C] = fa.flatMap(f)
Expand Down
3 changes: 1 addition & 2 deletions core/src/main/scala/cats/data/IorT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,7 @@ private[data] sealed trait IorTMonadError[F[_], A] extends MonadError[IorT[F, A,
override def handleErrorWith[B](iort: IorT[F, A, B])(f: A => IorT[F, A, B]): IorT[F, A, B] =
IorT(F0.flatMap(iort.value) {
case Ior.Left(a) => f(a).value
case r @ Ior.Right(_) => F0.pure(r)
case Ior.Both(a, _) => f(a).value // should a be combined with result ??
case r @ (Ior.Right(_) | Ior.Both(_, _)) => F0.pure(r)
})
}

Expand Down
7 changes: 7 additions & 0 deletions core/src/main/scala/cats/syntax/monadError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ package syntax
trait MonadErrorSyntax {
implicit final def catsSyntaxMonadError[F[_], E, A](fa: F[A])(implicit F: MonadError[F, E]): MonadErrorOps[F, E, A] =
new MonadErrorOps(fa)

implicit final def catsSyntaxMonadErrorRethrow[F[_], E, A](fea: F[Either[E, A]])(implicit F: MonadError[F, E]): MonadErrorRethrowOps[F, E, A] =
new MonadErrorRethrowOps(fea)
}

final class MonadErrorOps[F[_], E, A](val fa: F[A]) extends AnyVal {
Expand All @@ -16,3 +19,7 @@ final class MonadErrorOps[F[_], E, A](val fa: F[A]) extends AnyVal {
def adaptError(pf: PartialFunction[E, E])(implicit F: MonadError[F, E]): F[A] =
F.adaptError(fa)(pf)
}

final class MonadErrorRethrowOps[F[_], E, A](val fea: F[Either[E, A]]) extends AnyVal {
def rethrow(implicit F: MonadError[F, E]): F[A] = F.rethrow(fea)
}
3 changes: 3 additions & 0 deletions laws/src/main/scala/cats/laws/MonadErrorLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ trait MonadErrorLaws[F[_], E] extends ApplicativeErrorLaws[F, E] with MonadLaws[

def adaptErrorRaise[A](e: E, f: E => E): IsEq[F[A]] =
F.adaptError(F.raiseError[A](e))(PartialFunction(f)) <-> F.raiseError(f(e))

def rethrowAttempt[A](fa: F[A]): IsEq[F[A]] =
F.rethrow(F.attempt(fa)) <-> fa
}

object MonadErrorLaws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ trait MonadErrorTests[F[_], E] extends ApplicativeErrorTests[F, E] with MonadTes
"monadError ensure consistency" -> forAll(laws.monadErrorEnsureConsistency[A] _),
"monadError ensureOr consistency" -> forAll(laws.monadErrorEnsureOrConsistency[A] _),
"monadError adaptError pure" -> forAll(laws.adaptErrorPure[A] _),
"monadError adaptError raise" -> forAll(laws.adaptErrorRaise[A] _)
"monadError adaptError raise" -> forAll(laws.adaptErrorRaise[A] _),
"monadError rethrow attempt" -> forAll(laws.rethrowAttempt[A] _)
)
}
}
Expand Down