diff --git a/core/src/main/scala/cats/FlatMap.scala b/core/src/main/scala/cats/FlatMap.scala index 5a01bfb26f..79a88b2f70 100644 --- a/core/src/main/scala/cats/FlatMap.scala +++ b/core/src/main/scala/cats/FlatMap.scala @@ -21,11 +21,51 @@ import simulacrum.typeclass def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] /** - * also commonly called join + * Alias for [[flatMap]]. + */ + def >>=[A, B](fa: F[A])(f: A => F[B]): F[B] = flatMap(fa)(f) + + /** + * "flatten" a nested `F` of `F` structure into a single-layer `F` structure. + * + * This is also commonly called `join`. + * + * Example: + * {{{ + * scala> import cats.Eval + * scala> import cats.implicits._ + * + * scala> val nested: Eval[Eval[Int]] = Eval.now(Eval.now(3)) + * scala> val flattened: Eval[Int] = nested.flatten + * scala> flattened.value + * res0: Int = 3 + * }}} */ def flatten[A](ffa: F[F[A]]): F[A] = flatMap(ffa)(fa => fa) + /** Sequentially compose two actions, discarding any value produced by the first. */ + def followedBy[A, B](fa: F[A])(fb: F[B]): F[B] = flatMap(fa)(_ => fb) + + /** Alias for [[followedBy]]. */ + @inline final def >>[A, B](fa: F[A])(fb: F[B]): F[B] = followedBy(fa)(fb) + + /** + * Sequentially compose two actions, discarding any value produced by the first. This variant of + * [[followedBy]] also lets you define the evaluation strategy of the second action. For instance + * you can evaluate it only ''after'' the first action has finished: + * + * {{{ + * scala> import cats.Eval + * scala> import cats.implicits._ + * scala> val fa: Option[Int] = Some(3) + * scala> def fb: Option[String] = Some("foo") + * scala> fa.followedByEval(Eval.later(fb)) + * res0: Option[String] = Some(foo) + * }}} + */ + def followedByEval[A, B](fa: F[A])(fb: Eval[F[B]]): F[B] = flatMap(fa)(_ => fb.value) + override def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] = flatMap(ff)(f => map(fa)(f)) @@ -34,6 +74,13 @@ import simulacrum.typeclass /** * Pair `A` with the result of function application. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * scala> List("12", "34", "56").mproduct(_.toList) + * res0: List[(String, Char)] = List((12,1), (12,2), (34,3), (34,4), (56,5), (56,6)) + * }}} */ def mproduct[A, B](fa: F[A])(f: A => F[B]): F[(A, B)] = flatMap(fa)(a => map(f(a))((a, _))) diff --git a/core/src/main/scala/cats/syntax/flatMap.scala b/core/src/main/scala/cats/syntax/flatMap.scala index 0db04b7bf4..057ce263f4 100644 --- a/core/src/main/scala/cats/syntax/flatMap.scala +++ b/core/src/main/scala/cats/syntax/flatMap.scala @@ -2,13 +2,14 @@ package cats package syntax private[syntax] trait FlatMapSyntax1 { - implicit def catsSyntaxUFlatMap[FA](fa: FA)(implicit U: Unapply[FlatMap, FA]): FlatMapOps[U.M, U.A] = - new FlatMapOps[U.M, U.A](U.subst(fa))(U.TC) + implicit def catsSyntaxUFlatMap[FA](fa: FA)(implicit U: Unapply[FlatMap, FA]): FlatMap.Ops[U.M, U.A] = + new FlatMap.Ops[U.M, U.A]{ + val self = U.subst(fa) + val typeClassInstance = U.TC + } } -trait FlatMapSyntax extends FlatMapSyntax1 { - implicit def catsSyntaxFlatMap[F[_]: FlatMap, A](fa: F[A]): FlatMapOps[F, A] = - new FlatMapOps(fa) +trait FlatMapSyntax extends FlatMap.ToFlatMapOps with FlatMapSyntax1 { implicit def catsSyntaxFlatten[F[_]: FlatMap, A](ffa: F[F[A]]): FlattenOps[F, A] = new FlattenOps[F, A](ffa) @@ -17,47 +18,6 @@ trait FlatMapSyntax extends FlatMapSyntax1 { new IfMOps[F](fa) } -final class FlatMapOps[F[_], A](fa: F[A])(implicit F: FlatMap[F]) { - def flatMap[B](f: A => F[B]): F[B] = F.flatMap(fa)(f) - - /** - * Pair `A` with the result of function application. - * - * Example: - * {{{ - * scala> import cats.implicits._ - * scala> List("12", "34", "56").mproduct(_.toList) - * res0: List[(String, Char)] = List((12,1), (12,2), (34,3), (34,4), (56,5), (56,6)) - * }}} - */ - def mproduct[B](f: A => F[B]): F[(A, B)] = F.mproduct(fa)(f) - - def >>=[B](f: A => F[B]): F[B] = F.flatMap(fa)(f) - - /** Alias for [[followedBy]]. */ - @inline final def >>[B](fb: F[B]): F[B] = followedBy(fb) - - /** Sequentially compose two actions, discarding any value produced by the first. */ - def followedBy[B](fb: F[B]): F[B] = F.flatMap(fa)(_ => fb) - - /** - * Sequentially compose two actions, discarding any value produced by the first. This variant of - * [[followedBy]] also lets you define the evaluation strategy of the second action. For instance - * you can evaluate it only ''after'' the first action has finished: - * - * {{{ - * scala> import cats.Eval - * scala> import cats.implicits._ - * scala> val fa: Option[Int] = Some(3) - * scala> def fb: Option[String] = Some("foo") - * scala> fa.followedByEval(Eval.later(fb)) - * res0: Option[String] = Some(foo) - * }}} - */ - def followedByEval[B](fb: Eval[F[B]]): F[B] = F.flatMap(fa)(_ => fb.value) - -} - final class FlattenOps[F[_], A](ffa: F[F[A]])(implicit F: FlatMap[F]) { /**