Skip to content

Commit

Permalink
Add ensureP (partial function) method to MonadError (#2194)
Browse files Browse the repository at this point in the history
* Add ensureP method to MonadError

* Rename to `reject`. Fix Test.

* Use applyOrElse to potentially avoid evaluating PF twice.
  • Loading branch information
wogan authored and kailuowang committed Mar 16, 2018
1 parent 06041fb commit 3938d1d
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 12 deletions.
5 changes: 5 additions & 0 deletions core/src/main/scala/cats/syntax/monadError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ final class MonadErrorOps[F[_], E, A](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)

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

def adaptError(pf: PartialFunction[E, E])(implicit F: MonadError[F, E]): F[A] =
F.adaptError(fa)(pf)
}
Expand Down
47 changes: 35 additions & 12 deletions tests/src/test/scala/cats/tests/MonadErrorSuite.scala
Original file line number Diff line number Diff line change
@@ -1,36 +1,59 @@
package cats
package tests

import scala.util.{Failure, Success, Try}

class MonadErrorSuite extends CatsSuite {

val successful: Option[Int] = 42.some
val failed: Option[Int] = None
implicit val eqThrow: Eq[Throwable] = Eq.fromUniversalEquals

val successful: Try[Int] = Success(42)
val failedValue: Throwable = new IllegalArgumentException("default failure")
val otherValue: Throwable = new IllegalStateException("other failure")
val failed: Try[Int] = Failure(failedValue)

test("ensure raises an error if the predicate fails") {
successful.ensure(())(i => false) should === (None)
successful.ensure(failedValue)(_ => false) should === (failed)
}

test("ensure returns the successful value if the predicate succeeds") {
successful.ensure(())(i => true) should === (successful)
successful.ensure(failedValue)(_ => true) should === (successful)
}

test("ensure returns the failure, when applied to a failure") {
failed.ensure(())(i => false) should === (failed)
failed.ensure(())(i => true) should === (failed)
test("ensure returns the original failure, when applied to a failure") {
failed.ensure(otherValue)(_ => false) should === (failed)
failed.ensure(otherValue)(_ => true) should === (failed)
}

test("ensureOr raises an error if the predicate fails") {
successful.ensureOr(_ => ())(_ => false) should === (None)
successful.ensureOr(_ => failedValue)(_ => false) should === (failed)
}

test("ensureOr returns the successful value if the predicate succeeds") {
successful.ensureOr(_ => ())(_ => true) should === (successful)
successful.ensureOr(_ => failedValue)(_ => true) should === (successful)
}

test("ensureOr returns the original failure, when applied to a failure") {
failed.ensureOr(_ => otherValue)(_ => false) should === (failed)
failed.ensureOr(_ => otherValue)(_ => true) should === (failed)
}

test("ensureOr returns the failure, when applied to a failure") {
failed.ensureOr(_ => ())(_ => false) should === (failed)
failed.ensureOr(_ => ())(_ => true) should === (failed)
test("ensureP 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") {
failed.reject {
case i if i < 0 => otherValue
} should === (failed)
}

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

}

0 comments on commit 3938d1d

Please sign in to comment.