diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index 211b5597779..f4256113027 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -506,38 +506,6 @@ import Foldable.sentinel ) } - /** - * Separate this Foldable into a Tuple by an effectful separating function `A => G[Either[B, C]]` - * Equivalent to `Bitraversable#traverse` over `Alternative#separate` - * - * {{{ - * scala> import cats.implicits._ - * scala> val list = List(1,2,3,4) - * scala> val partitioned1 = Foldable[List].partitionEitherM(list)(a => if (a % 2 == 0) Eval.now(Either.left[String, Int](a.toString)) else Eval.now(Either.right[String, Int](a))) - * Since `Eval.now` yields a lazy computation, we need to force it to inspect the result: - * scala> partitioned1.value - * res0: (List[String], List[Int]) = (List(2, 4),List(1, 3)) - * scala> val partitioned2 = Foldable[List].partitionEitherM(list)(a => Eval.later(Either.right(a * 4))) - * scala> partitioned2.value - * res1: (List[Nothing], List[Int]) = (List(),List(4, 8, 12, 16)) - * }}} - */ - def partitionEitherM[G[_], A, B, C](fa: F[A])(f: A => G[Either[B, C]])(implicit A: Alternative[F], - M: Monad[G]): G[(F[B], F[C])] = { - import cats.instances.tuple._ - - implicit val mb: Monoid[F[B]] = A.algebra[B] - implicit val mc: Monoid[F[C]] = A.algebra[C] - - foldMapM[G, A, (F[B], F[C])](fa)( - a => - M.map(f(a)) { - case Right(c) => (A.empty[B], A.pure(c)) - case Left(b) => (A.pure(b), A.empty[C]) - } - ) - } - /** * Convert F[A] to a List[A], only including elements which match `p`. */ diff --git a/core/src/main/scala/cats/implicits.scala b/core/src/main/scala/cats/implicits.scala index 1267851ae60..1f78ae3cb4d 100644 --- a/core/src/main/scala/cats/implicits.scala +++ b/core/src/main/scala/cats/implicits.scala @@ -7,6 +7,7 @@ object implicits with syntax.AllSyntaxBinCompat2 with syntax.AllSyntaxBinCompat3 with syntax.AllSyntaxBinCompat4 + with syntax.AllSyntaxBinCompat5 with instances.AllInstances with instances.AllInstancesBinCompat0 with instances.AllInstancesBinCompat1 diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index 5a65ddeaf5d..1a65e022221 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -84,3 +84,5 @@ trait AllSyntaxBinCompat4 with ParallelApplySyntax with FoldableSyntaxBinCompat0 with ReducibleSyntaxBinCompat0 + +trait AllSyntaxBinCompat5 extends FoldableSyntaxBinCompat1 diff --git a/core/src/main/scala/cats/syntax/foldable.scala b/core/src/main/scala/cats/syntax/foldable.scala index 93e130d1423..9bede5f0f42 100644 --- a/core/src/main/scala/cats/syntax/foldable.scala +++ b/core/src/main/scala/cats/syntax/foldable.scala @@ -14,6 +14,11 @@ trait FoldableSyntaxBinCompat0 { new FoldableOps0[F, A](fa) } +trait FoldableSyntaxBinCompat1 { + implicit final def catsSyntaxFoldableBinCompat0[F[_]](fa: Foldable[F]): FoldableOpsBinCompat1[F] = + new FoldableOpsBinCompat1(fa) +} + final class NestedFoldableOps[F[_], G[_], A](private val fga: F[G[A]]) extends AnyVal { def sequence_(implicit F: Foldable[F], G: Applicative[G]): G[Unit] = F.sequence_(fga) @@ -195,7 +200,41 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal { case None ⇒ acc } ) +} + +final class FoldableOpsBinCompat1[F[_]](private val F: Foldable[F]) extends AnyVal { + /** + * Separate this Foldable into a Tuple by an effectful separating function `A => G[Either[B, C]]` + * Equivalent to `Bitraversable#traverse` over `Alternative#separate` + * + * {{{ + * scala> import cats.implicits._, cats.Foldable, cats.Eval + * scala> val list = List(1,2,3,4) + * scala> val partitioned1 = Foldable[List].partitionEitherM(list)(a => if (a % 2 == 0) Eval.now(Either.left[String, Int](a.toString)) else Eval.now(Either.right[String, Int](a))) + * Since `Eval.now` yields a lazy computation, we need to force it to inspect the result: + * scala> partitioned1.value + * res0: (List[String], List[Int]) = (List(2, 4),List(1, 3)) + * scala> val partitioned2 = Foldable[List].partitionEitherM(list)(a => Eval.later(Either.right(a * 4))) + * scala> partitioned2.value + * res1: (List[Nothing], List[Int]) = (List(),List(4, 8, 12, 16)) + * }}} + */ + def partitionEitherM[G[_], A, B, C](fa: F[A])(f: A => G[Either[B, C]])(implicit A: Alternative[F], + M: Monad[G]): G[(F[B], F[C])] = { + import cats.instances.tuple._ + + implicit val mb: Monoid[F[B]] = A.algebra[B] + implicit val mc: Monoid[F[C]] = A.algebra[C] + + F.foldMapM[G, A, (F[B], F[C])](fa)( + a => + M.map(f(a)) { + case Right(c) => (A.empty[B], A.pure(c)) + case Left(b) => (A.pure(b), A.empty[C]) + } + ) + } } final class FoldableOps0[F[_], A](val fa: F[A]) extends AnyVal {