Skip to content

Commit 5e92588

Browse files
committed
Add flatMapOrKeep to Monad
1 parent dfd9559 commit 5e92588

File tree

5 files changed

+28
-2
lines changed

5 files changed

+28
-2
lines changed

core/src/main/scala-2/cats/syntax/MonadOps.scala

+2
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ final class MonadOps[F[_], A](private val fa: F[A]) extends AnyVal {
3030
def untilM_(p: F[Boolean])(implicit M: Monad[F]): F[Unit] = M.untilM_(fa)(p)
3131
def iterateWhile(p: A => Boolean)(implicit M: Monad[F]): F[A] = M.iterateWhile(fa)(p)
3232
def iterateUntil(p: A => Boolean)(implicit M: Monad[F]): F[A] = M.iterateUntil(fa)(p)
33+
def flatMapOrKeep[A1 >: A](pfa: PartialFunction[A, F[A1]])(implicit M: Monad[F]): F[A1] =
34+
M.flatMapOrKeep[A, A1](fa)(pfa)
3335
}

core/src/main/scala-3/cats/syntax/MonadOps.scala

+2
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ final class MonadOps[F[_], A](private val fa: F[A]) extends AnyVal {
3030
def untilM_(using M: Monad[F])(p: F[Boolean]): F[Unit] = M.untilM_(fa)(p)
3131
def iterateWhile(using M: Monad[F])(p: A => Boolean): F[A] = M.iterateWhile(fa)(p)
3232
def iterateUntil(using M: Monad[F])(p: A => Boolean): F[A] = M.iterateUntil(fa)(p)
33+
def flatMapOrKeep[A1 >: A](using M: Monad[F])(pfa: PartialFunction[A, F[A1]]): F[A1] =
34+
M.flatMapOrKeep[A, A1](fa)(pfa)
3335
}

core/src/main/scala/cats/Monad.scala

+14
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,20 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] {
161161

162162
tailRecM(branches.toList)(step)
163163
}
164+
165+
/**
166+
* Modifies the `A` value in `F[A]` with the supplied function, if the function is defined for the value.
167+
* Example:
168+
* {{{
169+
* scala> import cats.syntax.all._
170+
*
171+
* scala> List(1, 2, 3).flatMapOrKeep{ case 2 => List(2, 22, 222) }
172+
* res0: List[Int] = List(1, 2, 22, 222, 3)
173+
* }}}
174+
*/
175+
def flatMapOrKeep[A, A1 >: A](fa: F[A])(pfa: PartialFunction[A, F[A1]]): F[A1] =
176+
flatMap(fa)(a => pfa.applyOrElse(a, pure[A1]))
177+
164178
}
165179

166180
object Monad {

laws/src/main/scala/cats/laws/MonadLaws.scala

+6
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ trait MonadLaws[F[_]] extends ApplicativeLaws[F] with FlatMapLaws[F] {
5757
def mapFlatMapCoherence[A, B](fa: F[A], f: A => B): IsEq[F[B]] =
5858
fa.flatMap(a => F.pure(f(a))) <-> fa.map(f)
5959

60+
/**
61+
* Make sure that flatMapOrKeep and flatMap are consistent.
62+
*/
63+
def flatMapOrKeepToFlatMapCoherence[A, A1 >: A](fa: F[A], pf: PartialFunction[A, F[A1]]): IsEq[F[A1]] =
64+
F.flatMapOrKeep(fa)(pf) <-> F.flatMap(fa)(a => pf.applyOrElse(a, F.pure[A1]))
65+
6066
lazy val tailRecMStackSafety: IsEq[F[Int]] = {
6167
val n = 50000
6268
val res = F.tailRecM(0)(i => F.pure(if (i < n) Either.left(i + 1) else Either.right(i)))

laws/src/main/scala/cats/laws/discipline/MonadTests.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ trait MonadTests[F[_]] extends ApplicativeTests[F] with FlatMapTests[F] {
5555
Seq(
5656
"monad left identity" -> forAll(laws.monadLeftIdentity[A, B] _),
5757
"monad right identity" -> forAll(laws.monadRightIdentity[A] _),
58-
"map flatMap coherence" -> forAll(laws.mapFlatMapCoherence[A, B] _)
58+
"map flatMap coherence" -> forAll(laws.mapFlatMapCoherence[A, B] _),
59+
"flatMapOrKeep flatMap coherence" -> forAll(laws.flatMapOrKeepToFlatMapCoherence[A, A] _)
5960
) ++ (if (Platform.isJvm) Seq[(String, Prop)]("tailRecM stack safety" -> Prop.lzy(laws.tailRecMStackSafety))
6061
else Seq.empty)
6162
}
@@ -84,7 +85,8 @@ trait MonadTests[F[_]] extends ApplicativeTests[F] with FlatMapTests[F] {
8485
Seq(
8586
"monad left identity" -> forAll(laws.monadLeftIdentity[A, B] _),
8687
"monad right identity" -> forAll(laws.monadRightIdentity[A] _),
87-
"map flatMap coherence" -> forAll(laws.mapFlatMapCoherence[A, B] _)
88+
"map flatMap coherence" -> forAll(laws.mapFlatMapCoherence[A, B] _),
89+
"flatMapOrKeep flatMap coherence" -> forAll(laws.flatMapOrKeepToFlatMapCoherence[A, A] _)
8890
)
8991
}
9092
}

0 commit comments

Comments
 (0)