Skip to content

Commit

Permalink
Add getOrRaise for OptionT, EitherT and IorT
Browse files Browse the repository at this point in the history
  • Loading branch information
david.geirola committed May 24, 2022
1 parent 3cb7639 commit bc395c6
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 1 deletion.
20 changes: 20 additions & 0 deletions core/src/main/scala/cats/data/EitherT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,26 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) {
case Right(b) => F.pure(b)
}

/***
*
* Like [[getOrElseF]] but accept an error `E` and raise it when the inner `Either` is `Left`
*
* Equivalent to `getOrElseF(F.raiseError(e)))`
*
* Example:
* {{{
* scala> import cats.data.EitherT
* scala> import cats.implicits._
* scala> import scala.util.{Success, Failure, Try}
* scala> val eitherT: EitherT[Try,String,Int] = EitherT[Try,String,Int](Success(Left("abc")))
* scala> eitherT.getOrRaise(new RuntimeException("ERROR!"))
* res0: Try[Int] = Failure(java.lang.RuntimeException: ERROR!)
* }}}
*/
def getOrRaise[E](e: => E)(implicit F: MonadError[F, _ >: E]): F[B] =
getOrElseF(F.raiseError(e))

/**
* Example:
* {{{
Expand Down
20 changes: 20 additions & 0 deletions core/src/main/scala/cats/data/IorT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,26 @@ final case class IorT[F[_], A, B](value: F[Ior[A, B]]) {
case Ior.Both(_, b) => F.pure(b)
}

/***
*
* Like [[getOrElseF]] but accept an error `E` and raise it when the inner `Ior` is `Left`
*
* Equivalent to `getOrElseF(F.raiseError(e)))`
*
* Example:
* {{{
* scala> import cats.data.IorT
* scala> import cats.implicits._
* scala> import scala.util.{Success, Failure, Try}
* scala> val iorT: IorT[Try,String,Int] = IorT.leftT("abc")
* scala> iorT.getOrRaise(new RuntimeException("ERROR!"))
* res0: Try[Int] = Failure(java.lang.RuntimeException: ERROR!)
* }}}
*/
def getOrRaise[E](e: => E)(implicit F: MonadError[F, _ >: E]): F[B] =
getOrElseF(F.raiseError(e))

def valueOr[BB >: B](f: A => BB)(implicit F: Functor[F], BB: Semigroup[BB]): F[BB] = F.map(value)(_.valueOr(f))

def forall(f: B => Boolean)(implicit F: Functor[F]): F[Boolean] = F.map(value)(_.forall(f))
Expand Down
18 changes: 18 additions & 0 deletions core/src/main/scala/cats/data/OptionT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,24 @@ final case class OptionT[F[_], A](value: F[Option[A]]) {
def getOrElseF[B >: A](default: => F[B])(implicit F: Monad[F]): F[B] =
F.flatMap(value)(_.fold(default)(F.pure))

/**
* Like [[getOrElseF]] but accept an error `E` and raise it when the inner `Option` is `None`
*
* Equivalent to `getOrElseF(F.raiseError(e)))`
*
* {{{
* scala> import cats.data.OptionT
* scala> import cats.implicits._
* scala> import scala.util.{Success, Failure, Try}
*
* scala> val optionT: OptionT[Try, Int] = OptionT[Try, Int](Success(None))
* scala> optionT.getOrRaise(new RuntimeException("ERROR!"))
* res0: Try[Int] = Failure(java.lang.RuntimeException: ERROR!)
* }}}
*/
def getOrRaise[E](e: => E)(implicit F: MonadError[F, _ >: E]): F[A] =
getOrElseF(F.raiseError(e))

/**
* Example:
* {{{
Expand Down
18 changes: 18 additions & 0 deletions tests/shared/src/test/scala/cats/tests/EitherTSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,24 @@ class EitherTSuite extends CatsSuite {
}
}

test("getOrRaise consistent with EitherT.getOrElseF(F.raiseError(e))") {
forAll { (eithert: EitherT[Either[String, *], String, Int], error: String) =>
assertEquals(
obtained = eithert.getOrRaise(error),
expected = eithert.getOrElseF(Left(error))
)
}
}

test("getOrRaise consistent with EitherT.leftMap(_ => error).rethrowT") {
forAll { (eithert: EitherT[Either[String, *], String, Int], error: String) =>
assertEquals(
obtained = eithert.getOrRaise(error),
expected = eithert.leftMap(_ => error).rethrowT
)
}
}

test("orElse with Id consistent with Either orElse") {
forAll { (eithert: EitherT[Id, String, Int], fallback: EitherT[Id, String, Int]) =>
assert(eithert.orElse(fallback).value === (eithert.value.orElse(fallback.value)))
Expand Down
11 changes: 10 additions & 1 deletion tests/shared/src/test/scala/cats/tests/IorTSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,16 @@ class IorTSuite extends CatsSuite {

test("getOrElseF with Id consistent with Ior getOrElse") {
forAll { (iort: IorT[Id, String, Int], i: Int) =>
assert(iort.getOrElseF(i) === (iort.value.getOrElse(i)))
assert(iort.getOrElseF(i) === iort.value.getOrElse(i))
}
}

test("getOrRaise consistent with IorT.getOrElseF(F.raiseError(e))") {
forAll { (iort: IorT[Either[String, *], String, Int], error: String) =>
assertEquals(
obtained = iort.getOrRaise(error),
expected = iort.getOrElseF(Left(error))
)
}
}

Expand Down
10 changes: 10 additions & 0 deletions tests/shared/src/test/scala/cats/tests/OptionTSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,16 @@ class OptionTSuite extends CatsSuite {
)
}
}

test("OptionT[Try, A].getOrRaise consistent with OptionT.getOrElseF(F.raiseError(e))") {
forAll { (o: Either[String, Option[Int]], error: String) =>
assertEquals(
obtained = OptionT[Either[String, *], Int](o).getOrRaise(error),
expected = OptionT[Either[String, *], Int](o).getOrElseF(Left(error))
)
}
}

test("OptionT[Id, A].collect consistent with Option.collect") {
forAll { (o: Option[Int], f: Int => Option[String]) =>
val p = Function.unlift(f)
Expand Down

0 comments on commit bc395c6

Please sign in to comment.