Skip to content

Commit

Permalink
Add when and unless to OptionT (#3233)
Browse files Browse the repository at this point in the history
* Add when and unless to OptionT

* Fix comments
  • Loading branch information
strong-zero authored and LukaJCB committed Jan 2, 2020
1 parent 371fe73 commit 534d392
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 1 deletion.
40 changes: 40 additions & 0 deletions core/src/main/scala/cats/data/OptionT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
42 changes: 41 additions & 1 deletion tests/src/test/scala/cats/tests/OptionTSuite.scala
Original file line number Diff line number Diff line change
@@ -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._
Expand Down Expand Up @@ -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))
Expand Down

0 comments on commit 534d392

Please sign in to comment.