From 35e9b1fdd210311b4bb1aaf7e6b62a8ec05e3dcb Mon Sep 17 00:00:00 2001 From: Dmitry Polienko Date: Wed, 13 Nov 2019 23:58:01 +0700 Subject: [PATCH 1/3] Add parFoldMapA --- core/src/main/scala/cats/Parallel.scala | 12 ++++++++++ core/src/main/scala/cats/implicits.scala | 1 + core/src/main/scala/cats/syntax/all.scala | 3 +++ core/src/main/scala/cats/syntax/package.scala | 1 + .../src/main/scala/cats/syntax/parallel.scala | 22 ++++++++++++++++++- .../src/test/scala/cats/tests/CatsSuite.scala | 1 + .../test/scala/cats/tests/ParallelSuite.scala | 8 +++++++ 7 files changed, 47 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/Parallel.scala b/core/src/main/scala/cats/Parallel.scala index 065ffbe0e2..8511c80a2a 100644 --- a/core/src/main/scala/cats/Parallel.scala +++ b/core/src/main/scala/cats/Parallel.scala @@ -312,6 +312,18 @@ object Parallel extends ParallelArityFunctions2 { P.sequential(ftab) } + /** + * Like `Foldable[A].foldMapA`, but uses the applicative instance + * corresponding to the Parallel instance instead + */ + def parFoldMapA[T[_]: Foldable, M[_], A, B: Monoid]( + ta: T[A] + )(f: A => M[B])(implicit P: Parallel[M]): M[B] = { + val fb: P.F[B] = + Foldable[T].foldMapA(ta)(a => P.parallel(f(a)))(P.applicative, implicitly) + P.sequential(fb) + } + /** * Like `Applicative[F].ap`, but uses the applicative instance * corresponding to the Parallel instance instead. diff --git a/core/src/main/scala/cats/implicits.scala b/core/src/main/scala/cats/implicits.scala index 5c2f3202b9..03b246f075 100644 --- a/core/src/main/scala/cats/implicits.scala +++ b/core/src/main/scala/cats/implicits.scala @@ -9,6 +9,7 @@ object implicits with syntax.AllSyntaxBinCompat4 with syntax.AllSyntaxBinCompat5 with syntax.AllSyntaxBinCompat6 + with syntax.AllSyntaxBinCompat7 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 e93083fe37..24cec1f0cb 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -10,6 +10,7 @@ abstract class AllSyntaxBinCompat with AllSyntaxBinCompat4 with AllSyntaxBinCompat5 with AllSyntaxBinCompat6 + with AllSyntaxBinCompat7 trait AllSyntax extends AlternativeSyntax @@ -94,3 +95,5 @@ trait AllSyntaxBinCompat4 trait AllSyntaxBinCompat5 extends ParallelBitraverseSyntax trait AllSyntaxBinCompat6 extends ParallelUnorderedTraverseSyntax + +trait AllSyntaxBinCompat7 extends ParallelFoldMapASyntax diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index 595573dce4..8db5df771d 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -47,6 +47,7 @@ package object syntax { with ParallelApplySyntax with ParallelBitraverseSyntax with ParallelUnorderedTraverseSyntax + with ParallelFoldMapASyntax object partialOrder extends PartialOrderSyntax object profunctor extends ProfunctorSyntax object reducible extends ReducibleSyntax with ReducibleSyntaxBinCompat0 diff --git a/core/src/main/scala/cats/syntax/parallel.scala b/core/src/main/scala/cats/syntax/parallel.scala index f21e4982bf..d83a395119 100644 --- a/core/src/main/scala/cats/syntax/parallel.scala +++ b/core/src/main/scala/cats/syntax/parallel.scala @@ -1,6 +1,16 @@ package cats.syntax -import cats.{Bitraverse, CommutativeApplicative, FlatMap, Foldable, Monad, Parallel, Traverse, UnorderedTraverse} +import cats.{ + Bitraverse, + CommutativeApplicative, + FlatMap, + Foldable, + Monad, + Monoid, + Parallel, + Traverse, + UnorderedTraverse +} trait ParallelSyntax extends TupleParallelSyntax { @@ -79,6 +89,11 @@ trait ParallelUnorderedTraverseSyntax { } +trait ParallelFoldMapASyntax { + implicit final def catsSyntaxParallelFoldMapA[T[_], A](ta: T[A]): ParallelFoldMapAOps[T, A] = + new ParallelFoldMapAOps[T, A](ta) +} + final class ParallelTraversableOps[T[_], A](private val ta: T[A]) extends AnyVal { def parTraverse[M[_]: Monad, B](f: A => M[B])(implicit T: Traverse[T], P: Parallel[M]): M[T[B]] = Parallel.parTraverse(ta)(f) @@ -176,3 +191,8 @@ final class ParallelLeftSequenceOps[T[_, _], M[_], A, B](private val tmab: T[M[A def parLeftSequence(implicit T: Bitraverse[T], P: Parallel[M]): M[T[A, B]] = Parallel.parLeftSequence(tmab) } + +final class ParallelFoldMapAOps[T[_], A](private val ma: T[A]) extends AnyVal { + def parFoldMapA[M[_], B](f: A => M[B])(implicit T: Foldable[T], B: Monoid[B], P: Parallel[M]): M[B] = + Parallel.parFoldMapA(ma)(f) +} diff --git a/tests/src/test/scala/cats/tests/CatsSuite.scala b/tests/src/test/scala/cats/tests/CatsSuite.scala index ff691f95b8..4e1d4bf9eb 100644 --- a/tests/src/test/scala/cats/tests/CatsSuite.scala +++ b/tests/src/test/scala/cats/tests/CatsSuite.scala @@ -53,6 +53,7 @@ trait CatsSuite with AllSyntaxBinCompat4 with AllSyntaxBinCompat5 with AllSyntaxBinCompat6 + with AllSyntaxBinCompat7 with StrictCatsEquality { implicit override val generatorDrivenConfig: PropertyCheckConfiguration = diff --git a/tests/src/test/scala/cats/tests/ParallelSuite.scala b/tests/src/test/scala/cats/tests/ParallelSuite.scala index 8d052df629..a064cbcbd2 100644 --- a/tests/src/test/scala/cats/tests/ParallelSuite.scala +++ b/tests/src/test/scala/cats/tests/ParallelSuite.scala @@ -230,6 +230,14 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest with Sc } } + test("ParFoldMapA should be equivalent to parTraverse map combineAll (where it exists)") { + forAll { (es: List[Int], f: Int => Either[String, String]) => + es.parFoldMapA(f) should ===( + es.parTraverse(f).map(_.combineAll) + ) + } + } + test("parAp accumulates errors in order") { val right: Either[String, Int => Int] = Left("Hello") Parallel.parAp(right)("World".asLeft) should ===(Left("HelloWorld")) From d5b96dd5f73fa44713f53e4281548e25b0d45e74 Mon Sep 17 00:00:00 2001 From: Dmitry Polienko Date: Thu, 14 Nov 2019 14:03:00 +0700 Subject: [PATCH 2/3] Clean up following MR discussion --- core/src/main/scala/cats/Parallel.scala | 8 ++++---- core/src/main/scala/cats/implicits.scala | 1 - core/src/main/scala/cats/syntax/all.scala | 4 +--- core/src/main/scala/cats/syntax/parallel.scala | 2 +- tests/src/test/scala/cats/tests/CatsSuite.scala | 1 - tests/src/test/scala/cats/tests/ParallelSuite.scala | 4 ++-- tests/src/test/scala/cats/tests/SyntaxSuite.scala | 6 ++++++ 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/src/main/scala/cats/Parallel.scala b/core/src/main/scala/cats/Parallel.scala index 8511c80a2a..d5060ff7a2 100644 --- a/core/src/main/scala/cats/Parallel.scala +++ b/core/src/main/scala/cats/Parallel.scala @@ -314,13 +314,13 @@ object Parallel extends ParallelArityFunctions2 { /** * Like `Foldable[A].foldMapA`, but uses the applicative instance - * corresponding to the Parallel instance instead + * corresponding to the Parallel instance instead. */ - def parFoldMapA[T[_]: Foldable, M[_], A, B: Monoid]( + def parFoldMapA[T[_], M[_], A, B]( ta: T[A] - )(f: A => M[B])(implicit P: Parallel[M]): M[B] = { + )(f: A => M[B])(implicit T: Foldable[T], P: Parallel[M], B: Monoid[B]): M[B] = { val fb: P.F[B] = - Foldable[T].foldMapA(ta)(a => P.parallel(f(a)))(P.applicative, implicitly) + Foldable[T].foldMapA(ta)(a => P.parallel(f(a)))(P.applicative, B) P.sequential(fb) } diff --git a/core/src/main/scala/cats/implicits.scala b/core/src/main/scala/cats/implicits.scala index 03b246f075..5c2f3202b9 100644 --- a/core/src/main/scala/cats/implicits.scala +++ b/core/src/main/scala/cats/implicits.scala @@ -9,7 +9,6 @@ object implicits with syntax.AllSyntaxBinCompat4 with syntax.AllSyntaxBinCompat5 with syntax.AllSyntaxBinCompat6 - with syntax.AllSyntaxBinCompat7 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 24cec1f0cb..fdebc3588a 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -10,7 +10,7 @@ abstract class AllSyntaxBinCompat with AllSyntaxBinCompat4 with AllSyntaxBinCompat5 with AllSyntaxBinCompat6 - with AllSyntaxBinCompat7 + with ParallelFoldMapASyntax trait AllSyntax extends AlternativeSyntax @@ -95,5 +95,3 @@ trait AllSyntaxBinCompat4 trait AllSyntaxBinCompat5 extends ParallelBitraverseSyntax trait AllSyntaxBinCompat6 extends ParallelUnorderedTraverseSyntax - -trait AllSyntaxBinCompat7 extends ParallelFoldMapASyntax diff --git a/core/src/main/scala/cats/syntax/parallel.scala b/core/src/main/scala/cats/syntax/parallel.scala index d83a395119..e7406fe063 100644 --- a/core/src/main/scala/cats/syntax/parallel.scala +++ b/core/src/main/scala/cats/syntax/parallel.scala @@ -193,6 +193,6 @@ final class ParallelLeftSequenceOps[T[_, _], M[_], A, B](private val tmab: T[M[A } final class ParallelFoldMapAOps[T[_], A](private val ma: T[A]) extends AnyVal { - def parFoldMapA[M[_], B](f: A => M[B])(implicit T: Foldable[T], B: Monoid[B], P: Parallel[M]): M[B] = + def parFoldMapA[M[_], B](f: A => M[B])(implicit T: Foldable[T], P: Parallel[M], B: Monoid[B]): M[B] = Parallel.parFoldMapA(ma)(f) } diff --git a/tests/src/test/scala/cats/tests/CatsSuite.scala b/tests/src/test/scala/cats/tests/CatsSuite.scala index 4e1d4bf9eb..ff691f95b8 100644 --- a/tests/src/test/scala/cats/tests/CatsSuite.scala +++ b/tests/src/test/scala/cats/tests/CatsSuite.scala @@ -53,7 +53,6 @@ trait CatsSuite with AllSyntaxBinCompat4 with AllSyntaxBinCompat5 with AllSyntaxBinCompat6 - with AllSyntaxBinCompat7 with StrictCatsEquality { implicit override val generatorDrivenConfig: PropertyCheckConfiguration = diff --git a/tests/src/test/scala/cats/tests/ParallelSuite.scala b/tests/src/test/scala/cats/tests/ParallelSuite.scala index a064cbcbd2..8069a776a0 100644 --- a/tests/src/test/scala/cats/tests/ParallelSuite.scala +++ b/tests/src/test/scala/cats/tests/ParallelSuite.scala @@ -232,8 +232,8 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest with Sc test("ParFoldMapA should be equivalent to parTraverse map combineAll (where it exists)") { forAll { (es: List[Int], f: Int => Either[String, String]) => - es.parFoldMapA(f) should ===( - es.parTraverse(f).map(_.combineAll) + Parallel.parFoldMapA(es)(f) should ===( + Parallel.parTraverse(es)(f).map(_.combineAll) ) } } diff --git a/tests/src/test/scala/cats/tests/SyntaxSuite.scala b/tests/src/test/scala/cats/tests/SyntaxSuite.scala index f2d20fa70a..04fb3f2f10 100644 --- a/tests/src/test/scala/cats/tests/SyntaxSuite.scala +++ b/tests/src/test/scala/cats/tests/SyntaxSuite.scala @@ -252,6 +252,12 @@ object SyntaxSuite val mtab2 = tmab.parLeftSequence } + def testParallelFoldable[T[_]: Foldable, M[_]: Parallel, A, B: Monoid]: Unit = { + val ta = mock[T[A]] + val f = mock[A => M[B]] + val mb = ta.parFoldMapA(f) + } + def testReducible[F[_]: Reducible, G[_]: Apply: SemigroupK, A: Semigroup, B, Z]: Unit = { val fa = mock[F[A]] val f1 = mock[(A, A) => A] From 0259244e01a0247875ee0b9e6c14165fe75a012c Mon Sep 17 00:00:00 2001 From: Dmitry Polienko Date: Thu, 14 Nov 2019 14:13:31 +0700 Subject: [PATCH 3/3] Move ParallelFoldMapASyntax from AllSyntaxBinCompat to AllSyntax --- core/src/main/scala/cats/syntax/all.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index fdebc3588a..c19ea72e45 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -10,7 +10,6 @@ abstract class AllSyntaxBinCompat with AllSyntaxBinCompat4 with AllSyntaxBinCompat5 with AllSyntaxBinCompat6 - with ParallelFoldMapASyntax trait AllSyntax extends AlternativeSyntax @@ -60,6 +59,7 @@ trait AllSyntax with ValidatedSyntax with VectorSyntax with WriterSyntax + with ParallelFoldMapASyntax trait AllSyntaxBinCompat0 extends UnorderedTraverseSyntax with ApplicativeErrorExtension with TrySyntax