diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 78cf504b77..48f58902ea 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -223,6 +223,46 @@ object OptionT extends OptionTInstances { */ def liftK[F[_]](implicit F: Functor[F]): F ~> OptionT[F, *] = λ[F ~> OptionT[F, *]](OptionT.liftF(_)) + + /** + * Creates a non-empty `OptionT[F, A]` from an `A` value if the given condition is `true`. + * Otherwise, `none[F, A]` is returned. Analogous to `Option.when`. + */ + def when[F[_], A](cond: Boolean)(a: => A)(implicit F: Applicative[F]): OptionT[F, A] = + if (cond) OptionT.some[F](a) else OptionT.none[F, A] + + /** + * Creates a non-empty `OptionT[F, A]` from an `F[A]` value if the given condition is `true`. + * Otherwise, the empty `OptionT[F, A]` is returned. Analogous to `Option.when`. + */ + def whenF[F[_], A](cond: Boolean)(fa: => F[A])(implicit F: Applicative[F]): OptionT[F, A] = + if (cond) OptionT.liftF(fa) else OptionT(F.map(fa)(_ => Option.empty)) + + /** + * Same as `whenF`, but expressed as a FunctionK for use with mapK. + */ + def whenK[F[_]](cond: Boolean)(implicit F: Applicative[F]): F ~> OptionT[F, *] = + λ[F ~> OptionT[F, *]](OptionT.whenF(cond)(_)) + + /** + * Creates a non-empty `OptionT[F, A]` from an `A` if the given condition is `false`. + * Otherwise, `none[F, A]` is returned. Analogous to `Option.unless`. + */ + def unless[F[_], A](cond: Boolean)(a: => A)(implicit F: Applicative[F]): OptionT[F, A] = + OptionT.when(!cond)(a) + + /** + * Creates an non-empty `OptionT[F, A]` from an `F[A]` if the given condition is `false`. + * Otherwise, the empty `OptionT[F, A]` is returned. Analogous to `Option.unless`. + */ + def unlessF[F[_], A](cond: Boolean)(fa: => F[A])(implicit F: Applicative[F]): OptionT[F, A] = + OptionT.whenF(!cond)(fa) + + /** + * Same as `unlessF`, but expressed as a FunctionK for use with mapK. + */ + def unlessK[F[_]](cond: Boolean)(implicit F: Applicative[F]): F ~> OptionT[F, *] = + λ[F ~> OptionT[F, *]](OptionT.unlessF(cond)(_)) } sealed abstract private[data] class OptionTInstances extends OptionTInstances0 { diff --git a/tests/src/test/scala/cats/tests/OptionTSuite.scala b/tests/src/test/scala/cats/tests/OptionTSuite.scala index 7a90868a2b..55ee37d0af 100644 --- a/tests/src/test/scala/cats/tests/OptionTSuite.scala +++ b/tests/src/test/scala/cats/tests/OptionTSuite.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.data.{Const, OptionT} +import cats.data.{Const, IdT, OptionT} import cats.kernel.{Monoid, Semigroup} import cats.kernel.laws.discipline.{EqTests, MonoidTests, OrderTests, PartialOrderTests, SemigroupTests} import cats.laws.discipline._ @@ -278,6 +278,46 @@ class OptionTSuite extends CatsSuite { } } + test("OptionT.when[Id, A] consistent with the same implementation of Option.when") { + val when = (c: Boolean, j: Int) => if (c) Some(j) else None + forAll { (i: Int, b: Boolean) => + OptionT.when[Id, Int](b)(i).value should ===(when(b, i)) + } + } + + test("OptionT.whenF[F, A] consistent with the same implementation of Option.when") { + val when = (c: Boolean, j: Int) => if (c) Some(j) else None + forAll { (li: List[Int], b: Boolean) => + OptionT.whenF(b)(li).value should ===(li.map(when(b, _))) + } + } + + test("OptionT.whenK and OptionT.whenF consistent") { + forAll { (li: List[Int], b: Boolean) => + IdT(li).mapK(OptionT.whenK(b)).value should ===(OptionT.whenF(b)(li)) + } + } + + test("OptionT.unless[Id, A] consistent with the same implementation of Option.unless") { + val unless = (c: Boolean, j: Int) => if (!c) Some(j) else None + forAll { (i: Int, b: Boolean) => + OptionT.unless[Id, Int](b)(i).value should ===(unless(b, i)) + } + } + + test("OptionT.unlessF[F, A] consistent with the same implementation of Option.unless") { + val unless = (c: Boolean, j: Int) => if (!c) Some(j) else None + forAll { (li: List[Int], b: Boolean) => + OptionT.unlessF(b)(li).value should ===(li.map(unless(b, _))) + } + } + + test("OptionT.unlessK and OptionT.unlessF consistent") { + forAll { (li: List[Int], b: Boolean) => + IdT(li).mapK(OptionT.unlessK(b)).value should ===(OptionT.unlessF(b)(li)) + } + } + test("orElse and orElseF consistent") { forAll { (o1: OptionT[List, Int], o2: OptionT[List, Int]) => o1.orElse(o2) should ===(o1.orElseF(o2.value))