Skip to content

Commit

Permalink
Add rethrowT method to EitherT (#2655)
Browse files Browse the repository at this point in the history
* add tests for `MonadError#rethrow` and correct descriptions of tests for `MonadError#reject`

* document `MonadErrorOps.reject` and refactor to one line

* Add `rethrowT` method to EitherT, inverse of `MonadError#attemptT`

* Reverse refactoring of MonadErrorOps.reject
  • Loading branch information
bplommer authored and kailuowang committed Jan 9, 2019
1 parent ee7d356 commit 17293ed
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 3 deletions.
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/data/EitherT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) {
case other => F.pure(other)
})

/**
* Inverse of `MonadError#attemptT`
*/
def rethrowT(implicit F: MonadError[F, A]): F[B] =
F.rethrow(value)

def valueOr[BB >: B](f: A => BB)(implicit F: Functor[F]): F[BB] = fold(f, identity)

def valueOrF[BB >: B](f: A => F[BB])(implicit F: Monad[F]): F[BB] =
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/scala/cats/syntax/monadError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ final class MonadErrorOps[F[_], E, A](private val fa: F[A]) extends AnyVal {
def ensureOr(error: A => E)(predicate: A => Boolean)(implicit F: MonadError[F, E]): F[A] =
F.ensureOr(fa)(error)(predicate)

/**
* Turns a successful value into the error returned by a given partial function if it is
* in the partial function's domain.
*/
def reject(pf: PartialFunction[A, E])(implicit F: MonadError[F, E]): F[A] =
F.flatMap(fa) { a =>
pf.andThen(F.raiseError[A]).applyOrElse(a, (_: A) => fa)
Expand Down
15 changes: 15 additions & 0 deletions tests/src/test/scala/cats/tests/EitherTSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import cats.data.EitherT
import cats.laws.discipline._
import cats.laws.discipline.arbitrary._
import cats.kernel.laws.discipline.{EqTests, MonoidTests, OrderTests, PartialOrderTests, SemigroupTests}
import scala.util.{Failure, Success, Try}

class EitherTSuite extends CatsSuite {
implicit val iso = SemigroupalTests.Isomorphisms
Expand Down Expand Up @@ -270,6 +271,20 @@ class EitherTSuite extends CatsSuite {
eithert.recoverWith { case "noteithert" => EitherT.pure[Id, String](5) } should ===(eithert)
}

test("rethrowT is inverse of attemptT when applied to a successful value") {
implicit val eqThrow: Eq[Throwable] = Eq.fromUniversalEquals
val success: Try[Int] = Success(42)

success.attemptT.rethrowT should ===(success)
}

test("rethrowT is inverse of attemptT when applied to a failed value") {
implicit val eqThrow: Eq[Throwable] = Eq.fromUniversalEquals
val failed: Try[Int] = Failure(new IllegalArgumentException("error"))

failed.attemptT.rethrowT should ===(failed)
}

test("transform consistent with value.map") {
forAll { (eithert: EitherT[List, String, Int], f: Either[String, Int] => Either[Long, Double]) =>
eithert.transform(f) should ===(EitherT(eithert.value.map(f)))
Expand Down
13 changes: 10 additions & 3 deletions tests/src/test/scala/cats/tests/MonadErrorSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,29 @@ class MonadErrorSuite extends CatsSuite {
failed.ensureOr(_ => otherValue)(_ => true) should ===(failed)
}

test("ensureP returns the successful value if the partial function is not defined") {
test("reject returns the successful value if the partial function is not defined") {
successful.reject {
case i if i < 0 => failedValue
} should ===(successful)
}

test("ensureP returns the original failure, when applied to a failure") {
test("reject returns the original failure, when applied to a failure") {
failed.reject {
case i if i < 0 => otherValue
} should ===(failed)
}

test("ensureP raises an error if the partial function is defined") {
test("reject raises an error if the partial function is defined") {
successful.reject {
case i if i > 0 => failedValue
} should ===(failed)
}

test("rethrow returns the failure, when applied to a Left of a failure") {
failed.attempt.rethrow should ===(failed)
}

test("rethrow returns the successful value, when applied to a Right of a successful value") {
successful.attempt.rethrow should ===(successful)
}
}

0 comments on commit 17293ed

Please sign in to comment.