From 101136642caeaa373842174ac0adce578be449bd Mon Sep 17 00:00:00 2001 From: Filippo Mariotti Date: Tue, 6 Mar 2018 01:10:20 +0000 Subject: [PATCH 1/8] Added parTupled boilerplate --- core/src/main/scala/cats/Parallel.scala | 2 +- project/Boilerplate.scala | 43 +++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/Parallel.scala b/core/src/main/scala/cats/Parallel.scala index 33f32bde1d..40300f01d1 100644 --- a/core/src/main/scala/cats/Parallel.scala +++ b/core/src/main/scala/cats/Parallel.scala @@ -109,7 +109,7 @@ object NonEmptyParallel { def apply[M[_], F[_]](implicit P: NonEmptyParallel[M, F]): NonEmptyParallel[M, F] = P } -object Parallel extends ParallelArityFunctions { +object Parallel extends ParallelArityFunctions with ParTupledArityFunctions { def apply[M[_], F[_]](implicit P: Parallel[M, F]): Parallel[M, F] = P diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index e787a6e65b..ff952f00b6 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -29,7 +29,8 @@ object Boilerplate { GenSemigroupalArityFunctions, GenApplyArityFunctions, GenTupleSemigroupalSyntax, - GenParallelArityFunctions, + GenParMapArityFunctions, + GenParTupledArityFunctions, GenTupleParallelSyntax ) @@ -213,7 +214,7 @@ object Boilerplate { } } - object GenParallelArityFunctions extends Template { + object GenParMapArityFunctions extends Template { def filename(root: File) = root / "cats" / "ParallelArityFunctions.scala" override def range = 2 to maxArity def content(tv: TemplateVals) = { @@ -246,6 +247,39 @@ object Boilerplate { } } + object GenParTupledArityFunctions extends Template { + def filename(root: File) = root / "cats" / "ParTupledArityFunctions.scala" + override def range = 2 to maxArity + def content(tv: TemplateVals) = { + import tv._ + + val tpes = synTypes map { tpe => s"M[$tpe]" } + val fargs = (0 until arity) map { "m" + _ } + val fparams = (fargs zip tpes) map { case (v,t) => s"$v:$t"} mkString ", " + val fargsS = fargs mkString ", " + + val nestedProducts = (0 until (arity - 2)).foldRight(s"Parallel.parProduct(m${arity - 2}, m${arity - 1})")((i, acc) => s"Parallel.parProduct(m$i, $acc)") + val `nested (a..n)` = (0 until (arity - 2)).foldRight(s"(a${arity - 2}, a${arity - 1})")((i, acc) => s"(a$i, $acc)") + + block""" + |package cats + | + |/** + | * @groupprio Ungrouped 0 + | * + | * @groupname ParTupledArity parTupled arity + | * @groupdesc ParTupledArity Higher-arity parTupled methods + | * @groupprio ParTupledArity 999 + | */ + |trait ParTupledArityFunctions { + - /** @group ParTupledArity */ + - def parTupled$arity[M[_], F[_], ${`A..N`}]($fparams)(implicit p: NonEmptyParallel[M, F]): M[(${`A..N`})] = + - p.flatMap.map($nestedProducts) { case ${`nested (a..n)`} => (${`a..n`}) } + |} + """ + } + } + object GenSemigroupalArityFunctions extends Template { def filename(root: File) = root / "cats" / "SemigroupalArityFunctions.scala" override def range = 2 to maxArity @@ -326,6 +360,10 @@ object Boilerplate { if (arity == 1) s"def parMap[F[_], Z](f: (${`A..N`}) => Z)(implicit p: NonEmptyParallel[M, F]): M[Z] = p.flatMap.map($tupleArgs)(f)" else s"def parMapN[F[_], Z](f: (${`A..N`}) => Z)(implicit p: NonEmptyParallel[M, F]): M[Z] = Parallel.parMap$arity($tupleArgs)(f)" + val parTupled = + if (arity == 1) "" + else s"def parTupledN[F[_]](implicit p: NonEmptyParallel[M, F]): M[(${`A..N`})] = Parallel.parTupled$arity($tupleArgs)" + block""" |package cats @@ -339,6 +377,7 @@ object Boilerplate { | -private[syntax] final class Tuple${arity}ParallelOps[M[_], ${`A..N`}]($tupleTpe) { - $parMap + - $parTupled -} | """ From 99fdcdf1838ce1c67d9a8a3a87d58b5d8b66a24a Mon Sep 17 00:00:00 2001 From: Filippo Mariotti Date: Tue, 6 Mar 2018 01:32:40 +0000 Subject: [PATCH 2/8] Refactored names --- core/src/main/scala/cats/Parallel.scala | 2 +- project/Boilerplate.scala | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/main/scala/cats/Parallel.scala b/core/src/main/scala/cats/Parallel.scala index 40300f01d1..d64657e5c8 100644 --- a/core/src/main/scala/cats/Parallel.scala +++ b/core/src/main/scala/cats/Parallel.scala @@ -109,7 +109,7 @@ object NonEmptyParallel { def apply[M[_], F[_]](implicit P: NonEmptyParallel[M, F]): NonEmptyParallel[M, F] = P } -object Parallel extends ParallelArityFunctions with ParTupledArityFunctions { +object Parallel extends ParallelArityFunctions with ParallelArityFunctions2 { def apply[M[_], F[_]](implicit P: Parallel[M, F]): Parallel[M, F] = P diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index ff952f00b6..c1a5a240bb 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -29,8 +29,8 @@ object Boilerplate { GenSemigroupalArityFunctions, GenApplyArityFunctions, GenTupleSemigroupalSyntax, - GenParMapArityFunctions, - GenParTupledArityFunctions, + GenParallelArityFunctions, + GenParallelArityFunctions2, GenTupleParallelSyntax ) @@ -214,7 +214,7 @@ object Boilerplate { } } - object GenParMapArityFunctions extends Template { + object GenParallelArityFunctions extends Template { def filename(root: File) = root / "cats" / "ParallelArityFunctions.scala" override def range = 2 to maxArity def content(tv: TemplateVals) = { @@ -247,8 +247,8 @@ object Boilerplate { } } - object GenParTupledArityFunctions extends Template { - def filename(root: File) = root / "cats" / "ParTupledArityFunctions.scala" + object GenParallelArityFunctions2 extends Template { + def filename(root: File) = root / "cats" / "ParallelArityFunctions2.scala" override def range = 2 to maxArity def content(tv: TemplateVals) = { import tv._ @@ -271,7 +271,7 @@ object Boilerplate { | * @groupdesc ParTupledArity Higher-arity parTupled methods | * @groupprio ParTupledArity 999 | */ - |trait ParTupledArityFunctions { + |trait ParallelArityFunctions2 { - /** @group ParTupledArity */ - def parTupled$arity[M[_], F[_], ${`A..N`}]($fparams)(implicit p: NonEmptyParallel[M, F]): M[(${`A..N`})] = - p.flatMap.map($nestedProducts) { case ${`nested (a..n)`} => (${`a..n`}) } From 9a8030d3151cba768b90d7109a2207c3f13e8a95 Mon Sep 17 00:00:00 2001 From: Filippo Mariotti Date: Tue, 6 Mar 2018 02:09:35 +0000 Subject: [PATCH 3/8] Changed tupled to tuple --- project/Boilerplate.scala | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index c1a5a240bb..f7e5f2fede 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -267,13 +267,13 @@ object Boilerplate { |/** | * @groupprio Ungrouped 0 | * - | * @groupname ParTupledArity parTupled arity - | * @groupdesc ParTupledArity Higher-arity parTupled methods - | * @groupprio ParTupledArity 999 + | * @groupname ParTupleArity parTuple arity + | * @groupdesc ParTupleArity Higher-arity parTuple methods + | * @groupprio ParTupleArity 999 | */ |trait ParallelArityFunctions2 { - - /** @group ParTupledArity */ - - def parTupled$arity[M[_], F[_], ${`A..N`}]($fparams)(implicit p: NonEmptyParallel[M, F]): M[(${`A..N`})] = + - /** @group ParTupleArity */ + - def parTuple$arity[M[_], F[_], ${`A..N`}]($fparams)(implicit p: NonEmptyParallel[M, F]): M[(${`A..N`})] = - p.flatMap.map($nestedProducts) { case ${`nested (a..n)`} => (${`a..n`}) } |} """ @@ -360,10 +360,9 @@ object Boilerplate { if (arity == 1) s"def parMap[F[_], Z](f: (${`A..N`}) => Z)(implicit p: NonEmptyParallel[M, F]): M[Z] = p.flatMap.map($tupleArgs)(f)" else s"def parMapN[F[_], Z](f: (${`A..N`}) => Z)(implicit p: NonEmptyParallel[M, F]): M[Z] = Parallel.parMap$arity($tupleArgs)(f)" - val parTupled = + val parTuple = if (arity == 1) "" - else s"def parTupledN[F[_]](implicit p: NonEmptyParallel[M, F]): M[(${`A..N`})] = Parallel.parTupled$arity($tupleArgs)" - + else s"def parTupleN[F[_]](implicit p: NonEmptyParallel[M, F]): M[(${`A..N`})] = Parallel.parTuple$arity($tupleArgs)" block""" |package cats @@ -377,7 +376,7 @@ object Boilerplate { | -private[syntax] final class Tuple${arity}ParallelOps[M[_], ${`A..N`}]($tupleTpe) { - $parMap - - $parTupled + - $parTuple -} | """ From e1a95a0b1b6f7641aa1d141615aa375389373126 Mon Sep 17 00:00:00 2001 From: Filippo Mariotti Date: Tue, 6 Mar 2018 02:59:06 +0000 Subject: [PATCH 4/8] Removed duplication for nested expansion --- project/Boilerplate.scala | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index f7e5f2fede..cecf688ef2 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -214,6 +214,11 @@ object Boilerplate { } } + final case class ParallelNestedExpansions(arity: Int) { + val products = (0 until (arity - 2)).foldRight(s"Parallel.parProduct(m${arity - 2}, m${arity - 1})")((i, acc) => s"Parallel.parProduct(m$i, $acc)") + val `(a..n)` = (0 until (arity - 2)).foldRight(s"(a${arity - 2}, a${arity - 1})")((i, acc) => s"(a$i, $acc)") + } + object GenParallelArityFunctions extends Template { def filename(root: File) = root / "cats" / "ParallelArityFunctions.scala" override def range = 2 to maxArity @@ -224,9 +229,7 @@ object Boilerplate { val fargs = (0 until arity) map { "m" + _ } val fparams = (fargs zip tpes) map { case (v,t) => s"$v:$t"} mkString ", " val fargsS = fargs mkString ", " - - val nestedProducts = (0 until (arity - 2)).foldRight(s"Parallel.parProduct(m${arity - 2}, m${arity - 1})")((i, acc) => s"Parallel.parProduct(m$i, $acc)") - val `nested (a..n)` = (0 until (arity - 2)).foldRight(s"(a${arity - 2}, a${arity - 1})")((i, acc) => s"(a$i, $acc)") + val nestedExpansion = ParallelNestedExpansions(arity) block""" |package cats @@ -241,7 +244,7 @@ object Boilerplate { |trait ParallelArityFunctions { - /** @group ParMapArity */ - def parMap$arity[M[_], F[_], ${`A..N`}, Z]($fparams)(f: (${`A..N`}) => Z)(implicit p: NonEmptyParallel[M, F]): M[Z] = - - p.flatMap.map($nestedProducts) { case ${`nested (a..n)`} => f(${`a..n`}) } + - p.flatMap.map(${nestedExpansion.products}) { case ${nestedExpansion.`(a..n)`} => f(${`a..n`}) } |} """ } @@ -257,9 +260,7 @@ object Boilerplate { val fargs = (0 until arity) map { "m" + _ } val fparams = (fargs zip tpes) map { case (v,t) => s"$v:$t"} mkString ", " val fargsS = fargs mkString ", " - - val nestedProducts = (0 until (arity - 2)).foldRight(s"Parallel.parProduct(m${arity - 2}, m${arity - 1})")((i, acc) => s"Parallel.parProduct(m$i, $acc)") - val `nested (a..n)` = (0 until (arity - 2)).foldRight(s"(a${arity - 2}, a${arity - 1})")((i, acc) => s"(a$i, $acc)") + val nestedExpansion = ParallelNestedExpansions(arity) block""" |package cats @@ -274,7 +275,7 @@ object Boilerplate { |trait ParallelArityFunctions2 { - /** @group ParTupleArity */ - def parTuple$arity[M[_], F[_], ${`A..N`}]($fparams)(implicit p: NonEmptyParallel[M, F]): M[(${`A..N`})] = - - p.flatMap.map($nestedProducts) { case ${`nested (a..n)`} => (${`a..n`}) } + - p.flatMap.map(${nestedExpansion.products}) { case ${nestedExpansion.`(a..n)`} => (${`a..n`}) } |} """ } From e99b5e42c2ae7a9ca73a890df0ebdc8bef1c4ce9 Mon Sep 17 00:00:00 2001 From: Filippo Mariotti Date: Tue, 6 Mar 2018 10:19:12 +0000 Subject: [PATCH 5/8] Renamed syntax to parTupled --- project/Boilerplate.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index cecf688ef2..182255f7f7 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -361,9 +361,9 @@ object Boilerplate { if (arity == 1) s"def parMap[F[_], Z](f: (${`A..N`}) => Z)(implicit p: NonEmptyParallel[M, F]): M[Z] = p.flatMap.map($tupleArgs)(f)" else s"def parMapN[F[_], Z](f: (${`A..N`}) => Z)(implicit p: NonEmptyParallel[M, F]): M[Z] = Parallel.parMap$arity($tupleArgs)(f)" - val parTuple = + val parTupled = if (arity == 1) "" - else s"def parTupleN[F[_]](implicit p: NonEmptyParallel[M, F]): M[(${`A..N`})] = Parallel.parTuple$arity($tupleArgs)" + else s"def parTupled[F[_]](implicit p: NonEmptyParallel[M, F]): M[(${`A..N`})] = Parallel.parTuple$arity($tupleArgs)" block""" |package cats @@ -377,7 +377,7 @@ object Boilerplate { | -private[syntax] final class Tuple${arity}ParallelOps[M[_], ${`A..N`}]($tupleTpe) { - $parMap - - $parTuple + - $parTupled -} | """ From b74cc6758c77a87b1f3c99c44ce590cd3fb2313e Mon Sep 17 00:00:00 2001 From: Filippo Mariotti Date: Tue, 6 Mar 2018 21:08:38 +0000 Subject: [PATCH 6/8] Changed ParallelArityFunctions2 to abstract class --- core/src/main/scala/cats/Parallel.scala | 2 +- project/Boilerplate.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/Parallel.scala b/core/src/main/scala/cats/Parallel.scala index d64657e5c8..3d48d4fe03 100644 --- a/core/src/main/scala/cats/Parallel.scala +++ b/core/src/main/scala/cats/Parallel.scala @@ -109,7 +109,7 @@ object NonEmptyParallel { def apply[M[_], F[_]](implicit P: NonEmptyParallel[M, F]): NonEmptyParallel[M, F] = P } -object Parallel extends ParallelArityFunctions with ParallelArityFunctions2 { +object Parallel extends ParallelArityFunctions2 { def apply[M[_], F[_]](implicit P: Parallel[M, F]): Parallel[M, F] = P diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index 182255f7f7..6f4dda8746 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -272,7 +272,7 @@ object Boilerplate { | * @groupdesc ParTupleArity Higher-arity parTuple methods | * @groupprio ParTupleArity 999 | */ - |trait ParallelArityFunctions2 { + |abstract class ParallelArityFunctions2 extends ParallelArityFunctions { - /** @group ParTupleArity */ - def parTuple$arity[M[_], F[_], ${`A..N`}]($fparams)(implicit p: NonEmptyParallel[M, F]): M[(${`A..N`})] = - p.flatMap.map(${nestedExpansion.products}) { case ${nestedExpansion.`(a..n)`} => (${`a..n`}) } From d4fdecc3c9d68733d740ba99b98f4e2ab34faf8b Mon Sep 17 00:00:00 2001 From: Filippo Mariotti Date: Sat, 10 Mar 2018 18:23:53 +0000 Subject: [PATCH 7/8] Added some tests for parTupled --- .../test/scala/cats/tests/ParallelSuite.scala | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/src/test/scala/cats/tests/ParallelSuite.scala b/tests/src/test/scala/cats/tests/ParallelSuite.scala index 8796ff7f0c..fc67d4b499 100644 --- a/tests/src/test/scala/cats/tests/ParallelSuite.scala +++ b/tests/src/test/scala/cats/tests/ParallelSuite.scala @@ -169,6 +169,36 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest { } } + test("ParTupled of NonEmptyList should be consistent with ParMap of Tuple.apply") { + forAll { (fa: NonEmptyList[Int], fb: NonEmptyList[Int], fc: NonEmptyList[Int], fd: NonEmptyList[Int]) => + (fa, fb, fc, fd).parTupled should ===((fa, fb, fc, fd).parMapN(Tuple4.apply)) + } + } + + test("ParTupled of NonEmptyVector should be consistent with ParMap of Tuple.apply") { + forAll { (fa: NonEmptyVector[Int], fb: NonEmptyVector[Int], fc: NonEmptyVector[Int], fd: NonEmptyVector[Int]) => + (fa, fb, fc, fd).parTupled should ===((fa, fb, fc, fd).parMapN(Tuple4.apply)) + } + } + + test("ParTupled of List should be consistent with ParMap of Tuple.apply") { + forAll { (fa: List[Int], fb: List[Int], fc: List[Int], fd: List[Int]) => + (fa, fb, fc, fd).parTupled should ===((fa, fb, fc, fd).parMapN(Tuple4.apply)) + } + } + + test("ParTupled of Vector should be consistent with ParMap of Tuple.apply") { + forAll { (fa: Vector[Int], fb: Vector[Int], fc: Vector[Int], fd: Vector[Int]) => + (fa, fb, fc, fd).parTupled should ===((fa, fb, fc, fd).parMapN(Tuple4.apply)) + } + } + + test("ParTupled of Stream should be consistent with ParMap of Tuple.apply") { + forAll { (fa: Stream[Int], fb: Stream[Int], fc: Stream[Int], fd: Stream[Int]) => + (fa, fb, fc, fd).parTupled should === ((fa, fb, fc, fd).parMapN(Tuple4.apply)) + } + } + test("IorT leverages parallel effect instances when it exists") { case class Marker(value: String) extends Exception("marker") { override def fillInStackTrace: Throwable = null From 94d069bbf5eec659ed765dad0cfa513b29d367f6 Mon Sep 17 00:00:00 2001 From: Filippo Mariotti Date: Sat, 10 Mar 2018 19:18:09 +0000 Subject: [PATCH 8/8] Added more tests --- .../test/scala/cats/tests/ParallelSuite.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/src/test/scala/cats/tests/ParallelSuite.scala b/tests/src/test/scala/cats/tests/ParallelSuite.scala index fc67d4b499..cf393fdd5a 100644 --- a/tests/src/test/scala/cats/tests/ParallelSuite.scala +++ b/tests/src/test/scala/cats/tests/ParallelSuite.scala @@ -199,6 +199,24 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest { } } + test("ParTupled of List should be consistent with zip") { + forAll { (fa: List[Int], fb: List[Int], fc: List[Int], fd: List[Int]) => + (fa, fb, fc, fd).parTupled should === (fa.zip(fb).zip(fc).zip(fd).map { case (((a, b), c), d) => (a, b, c, d) }) + } + } + + test("ParTupled of Vector should be consistent with zip") { + forAll { (fa: Vector[Int], fb: Vector[Int], fc: Vector[Int], fd: Vector[Int]) => + (fa, fb, fc, fd).parTupled should === (fa.zip(fb).zip(fc).zip(fd).map { case (((a, b), c), d) => (a, b, c, d) }) + } + } + + test("ParTupled of Stream should be consistent with zip") { + forAll { (fa: Stream[Int], fb: Stream[Int], fc: Stream[Int], fd: Stream[Int]) => + (fa, fb, fc, fd).parTupled should === (fa.zip(fb).zip(fc).zip(fd).map { case (((a, b), c), d) => (a, b, c, d) }) + } + } + test("IorT leverages parallel effect instances when it exists") { case class Marker(value: String) extends Exception("marker") { override def fillInStackTrace: Throwable = null