Skip to content

Commit

Permalink
Add ifTrueM, ifFalseM, ensureTrue and ensureFalse
Browse files Browse the repository at this point in the history
  • Loading branch information
geirolz committed Jul 19, 2024
1 parent dd89133 commit 5cdf69f
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 0 deletions.
5 changes: 5 additions & 0 deletions core/src/main/scala-2/cats/syntax/MonadOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

package cats.syntax

import cats.kernel.Monoid
import cats.{Alternative, Monad}

final class MonadOps[F[_], A](private val fa: F[A]) extends AnyVal {
Expand All @@ -32,4 +33,8 @@ final class MonadOps[F[_], A](private val fa: F[A]) extends AnyVal {
def iterateUntil(p: A => Boolean)(implicit M: Monad[F]): F[A] = M.iterateUntil(fa)(p)
def flatMapOrKeep[A1 >: A](pfa: PartialFunction[A, F[A1]])(implicit M: Monad[F]): F[A1] =
M.flatMapOrKeep[A, A1](fa)(pfa)
def ifTrueM[B: Monoid](ifTrue: => F[B])(implicit env: F[A] <:< F[Boolean], M: Monad[F]): F[B] =
M.ifTrueM(fa)(ifTrue)
def ifFalseM[B: Monoid](ifFalse: => F[B])(implicit env: F[A] <:< F[Boolean],M: Monad[F]): F[B] =
M.ifFalseM(fa)(ifFalse)
}
14 changes: 14 additions & 0 deletions core/src/main/scala/cats/Monad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

package cats

import cats.kernel.Monoid

/**
* Monad.
*
Expand Down Expand Up @@ -162,6 +164,18 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] {
tailRecM(branches.toList)(step)
}

/**
* If the `F[Boolean]` is `true` then return `ifTrue` otherwise return `ifFalse`
*/
def ifTrueM[B: Monoid](fa: F[Boolean])(ifTrue: => F[B]): F[B] =
ifM(fa)(ifTrue, pure(Monoid[B].empty))

/**
* If the `F[Boolean]` is `false` then return `ifFalse` otherwise return `Monoid[A].empty`
*/
def ifFalseM[B: Monoid](fa: F[Boolean])(ifFalse: => F[B]): F[B] =
ifM(fa)(pure(Monoid[B].empty), ifFalse)

/**
* Modifies the `A` value in `F[A]` with the supplied function, if the function is defined for the value.
* Example:
Expand Down
12 changes: 12 additions & 0 deletions core/src/main/scala/cats/MonadError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] {
def ensureOr[A](fa: F[A])(error: A => E)(predicate: A => Boolean): F[A] =
flatMap(fa)(a => if (predicate(a)) pure(a) else raiseError(error(a)))

/**
* Ensures that a `F[Boolean]` is `true`, otherwise raises an error.
*/
def ensureTrue(fa: F[Boolean])(error: => E): F[Boolean] =
ensure(fa)(error)(identity)

/**
* Ensures that a `F[Boolean]` is `false`, otherwise raises an error.
*/
def ensureFalse(fa: F[Boolean])(error: => E): F[Boolean] =
ensure(fa)(error)(bool => !bool)

/**
* Inverse of `attempt`
*
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 @@ -33,12 +33,19 @@ trait MonadErrorSyntax {
}

final class MonadErrorOps[F[_], E, A](private val fa: F[A]) extends AnyVal {

def ensure(error: => E)(predicate: A => Boolean)(implicit F: MonadError[F, E]): F[A] =
F.ensure(fa)(error)(predicate)

def ensureOr(error: A => E)(predicate: A => Boolean)(implicit F: MonadError[F, E]): F[A] =
F.ensureOr(fa)(error)(predicate)

def ensureTrue(error: => E)(implicit env: F[A] <:< F[Boolean], F: MonadError[F, E]): F[Boolean] =
F.ensureTrue(fa)(error)

def ensureFalse(error: => E)(implicit env: F[A] <:< F[Boolean], F: MonadError[F, E]): F[Boolean] =
F.ensureFalse(fa)(error)

/**
* Turns a successful value into the error returned by a given partial function if it is
* in the partial function's domain.
Expand Down
10 changes: 10 additions & 0 deletions tests/shared/src/test/scala/cats/tests/MonadErrorSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,14 @@ class MonadErrorSuite extends CatsSuite {
test("rethrow returns the successful value, when applied to a Right of a specialized successful value") {
assert(successful.attempt.asInstanceOf[Try[Either[IllegalArgumentException, Int]]].rethrow === successful)
}

test("ensureTrue raise an error only when the value is true") {
Try(true).ensureTrue(failedValue) === Failure(failedValue)
Try(false).ensureTrue(failedValue) === Success(false)
}

test("ensureFalse raise an error only when the value is false") {
Try(true).ensureFalse(failedValue) === Success(true)
Try(false).ensureFalse(failedValue) === Failure(failedValue)
}
}
15 changes: 15 additions & 0 deletions tests/shared/src/test/scala/cats/tests/MonadSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,19 @@ class MonadSuite extends CatsSuite {
assert(actual.value === 2)
}

test("ifTrueM"){
val actual1: Eval[Int] = Eval.later(true).ifTrueM(Eval.later(1))
assert(actual1.value === 1)

val actual2: Eval[Int] = Eval.later(false).ifTrueM(Eval.later(1))
assert(actual2.value === 0)
}

test("ifFalseM"){
val actual1: Eval[Int] = Eval.later(true).ifFalseM(Eval.later(1))
assert(actual1.value === 0)

val actual2: Eval[Int] = Eval.later(false).ifFalseM(Eval.later(1))
assert(actual2.value === 1)
}
}

0 comments on commit 5cdf69f

Please sign in to comment.