From e4404d768e4be0d8dec146a3c398f37cc7f4c093 Mon Sep 17 00:00:00 2001 From: James Collier Date: Sat, 2 Jan 2021 21:43:33 +0000 Subject: [PATCH 01/11] add sliding2 for foldable --- core/src/main/scala/cats/Foldable.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index cf2b3f5b60..268ddb00da 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -772,6 +772,16 @@ import scala.annotation.implicitNotFound case Some(a) => a } + def sliding2[A](fa: F[A]): List[(A, A)] = + foldRight(fa, Now((List.empty[(A, A)], None: Option[A]))) { (x2, eval) => + eval.value match { + case (acc, Some(x1)) => + Now(((x1, x2) :: acc, Some(x2))) + case (acc, None) => + Now((acc, Some(x2))) + } + }.value._1 + protected def intersperseList[A](xs: List[A], x: A): List[A] = { val bld = List.newBuilder[A] val it = xs.iterator From bd531ad9d8eb4a890f8dfd403a1a5ad8ba877187 Mon Sep 17 00:00:00 2001 From: James Collier Date: Sat, 2 Jan 2021 22:30:06 +0000 Subject: [PATCH 02/11] add 'arity' functions --- core/src/main/scala/cats/Foldable.scala | 4 +-- project/Boilerplate.scala | 40 ++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index 268ddb00da..6e43f970fd 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -30,7 +30,7 @@ import scala.annotation.implicitNotFound * See: [[http://www.cs.nott.ac.uk/~pszgmh/fold.pdf A tutorial on the universality and expressiveness of fold]] */ @implicitNotFound("Could not find an instance of Foldable for ${F}") -@typeclass trait Foldable[F[_]] extends UnorderedFoldable[F] { self => +@typeclass trait Foldable[F[_]] extends UnorderedFoldable[F] with FoldableArityFunctions[F] { self => /** * Left associative fold on 'F' using the function 'f'. @@ -781,7 +781,7 @@ import scala.annotation.implicitNotFound Now((acc, Some(x2))) } }.value._1 - + protected def intersperseList[A](xs: List[A], x: A): List[A] = { val bld = List.newBuilder[A] val it = xs.iterator diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index 338789542d..cabb9a41c7 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -28,7 +28,8 @@ object Boilerplate { GenTupleSemigroupalSyntax, GenParallelArityFunctions, GenParallelArityFunctions2, - GenTupleParallelSyntax + GenTupleParallelSyntax, + GenFoldableArityFunctions ) val header = "// auto-generated boilerplate by /project/Boilerplate.scala" // TODO: put something meaningful here? @@ -503,4 +504,41 @@ object Boilerplate { } } + object GenFoldableArityFunctions extends Template { + def filename(root: File) = root / "cats" / "FoldableArityFunctions.scala" + override def range = 3 to maxArity + def content(tv: TemplateVals) = { + import tv._ + + val tupleTpe = (1 to arity).map(_ => "A").mkString("(", ", ", ")") + def listXN(range: Range) = range.map("x" + _).mkString(" :: ") + val reverseXN = listXN(1 to arity - 1) + val tupleXN = (1 to arity).map("x" + _).mkString("(", ", ", ")") + + block""" + |package cats + | + |/** + | * @groupprio Ungrouped 0 + | * + | * @groupname FoldableSlidingN foldable arity + | * @groupdesc FoldableSlidingN Sliding windows of size N + | * @groupprio FoldableSlidingN 999 + | * + | */ + |trait FoldableArityFunctions[F[_]] { self: Foldable[F] => + - /** @group FoldableArity */ + - def sliding$arity[A](fa: F[A]): List[$tupleTpe] = + - foldRight(fa, Now((List.empty[$tupleTpe], List.empty[A]))) { (x$arity, eval) => + - eval.value match { + - case (acc, ${listXN(arity - 1 to 1 by -1)} :: Nil) => + - Now(($tupleXN :: acc, ${listXN(arity to 2 by -1)} :: Nil)) + - case (acc, l) => + - Now((acc, x$arity :: l)) + - } + - }.value._1 + |} + """ + } + } } From 2a06a52030a69bc3dcd120d39a9b30c268048c69 Mon Sep 17 00:00:00 2001 From: James Collier Date: Sat, 2 Jan 2021 23:04:31 +0000 Subject: [PATCH 03/11] fix sliding order --- core/src/main/scala/cats/Foldable.scala | 8 ++++---- project/Boilerplate.scala | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index 6e43f970fd..69a95f0fd1 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -773,12 +773,12 @@ import scala.annotation.implicitNotFound } def sliding2[A](fa: F[A]): List[(A, A)] = - foldRight(fa, Now((List.empty[(A, A)], None: Option[A]))) { (x2, eval) => + foldRight(fa, Now((List.empty[(A, A)], None: Option[A]))) { (x1, eval) => eval.value match { - case (acc, Some(x1)) => - Now(((x1, x2) :: acc, Some(x2))) + case (acc, Some(x2)) => + Now(((x1, x2) :: acc, Some(x1))) case (acc, None) => - Now((acc, Some(x2))) + Now((acc, Some(x1))) } }.value._1 diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index cabb9a41c7..1776ab2e86 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -529,12 +529,12 @@ object Boilerplate { |trait FoldableArityFunctions[F[_]] { self: Foldable[F] => - /** @group FoldableArity */ - def sliding$arity[A](fa: F[A]): List[$tupleTpe] = - - foldRight(fa, Now((List.empty[$tupleTpe], List.empty[A]))) { (x$arity, eval) => + - foldRight(fa, Now((List.empty[$tupleTpe], List.empty[A]))) { (x1, eval) => - eval.value match { - - case (acc, ${listXN(arity - 1 to 1 by -1)} :: Nil) => - - Now(($tupleXN :: acc, ${listXN(arity to 2 by -1)} :: Nil)) + - case (acc, ${listXN(2 to arity)} :: Nil) => + - Now(($tupleXN :: acc, ${listXN(1 to arity - 1)} :: Nil)) - case (acc, l) => - - Now((acc, x$arity :: l)) + - Now((acc, x1 :: l)) - } - }.value._1 |} From 52b697949de0e66686ca39b1586d64c146852b33 Mon Sep 17 00:00:00 2001 From: James Collier Date: Sat, 2 Jan 2021 23:40:28 +0000 Subject: [PATCH 04/11] use gen sliding2 --- core/src/main/scala/cats/Foldable.scala | 10 ---------- project/Boilerplate.scala | 9 +++++---- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index 69a95f0fd1..45fb82ddf6 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -772,16 +772,6 @@ import scala.annotation.implicitNotFound case Some(a) => a } - def sliding2[A](fa: F[A]): List[(A, A)] = - foldRight(fa, Now((List.empty[(A, A)], None: Option[A]))) { (x1, eval) => - eval.value match { - case (acc, Some(x2)) => - Now(((x1, x2) :: acc, Some(x1))) - case (acc, None) => - Now((acc, Some(x1))) - } - }.value._1 - protected def intersperseList[A](xs: List[A], x: A): List[A] = { val bld = List.newBuilder[A] val it = xs.iterator diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index 1776ab2e86..749ceba902 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -506,7 +506,7 @@ object Boilerplate { object GenFoldableArityFunctions extends Template { def filename(root: File) = root / "cats" / "FoldableArityFunctions.scala" - override def range = 3 to maxArity + override def range = 2 to maxArity def content(tv: TemplateVals) = { import tv._ @@ -530,10 +530,11 @@ object Boilerplate { - /** @group FoldableArity */ - def sliding$arity[A](fa: F[A]): List[$tupleTpe] = - foldRight(fa, Now((List.empty[$tupleTpe], List.empty[A]))) { (x1, eval) => - - eval.value match { - - case (acc, ${listXN(2 to arity)} :: Nil) => + - val (acc, l) = eval.value + - l match { + - case ${listXN(2 to arity)} :: Nil => - Now(($tupleXN :: acc, ${listXN(1 to arity - 1)} :: Nil)) - - case (acc, l) => + - case l => - Now((acc, x1 :: l)) - } - }.value._1 From 2d5c2facbf5857a90d2ea1a9d74b496404a73510 Mon Sep 17 00:00:00 2001 From: James Collier Date: Sun, 3 Jan 2021 18:55:35 +0000 Subject: [PATCH 05/11] tests for slidingN --- project/Boilerplate.scala | 2 +- .../test/scala/cats/tests/FoldableSuite.scala | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index 749ceba902..be13181f91 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -527,7 +527,7 @@ object Boilerplate { | * | */ |trait FoldableArityFunctions[F[_]] { self: Foldable[F] => - - /** @group FoldableArity */ + - /** @group FoldableSlidingN */ - def sliding$arity[A](fa: F[A]): List[$tupleTpe] = - foldRight(fa, Now((List.empty[$tupleTpe], List.empty[A]))) { (x1, eval) => - val (acc, l) = eval.value diff --git a/tests/src/test/scala/cats/tests/FoldableSuite.scala b/tests/src/test/scala/cats/tests/FoldableSuite.scala index 59877e12f7..25d921ca67 100644 --- a/tests/src/test/scala/cats/tests/FoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/FoldableSuite.scala @@ -281,6 +281,84 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit ) } } + + test(s"Foldable[$name].sliding2 consistent with List#sliding(2)") { + forAll { (fi: F[Int]) => + val n = 2 + (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding2) { case x1 :: x2 :: Nil => + (x1, x2) + } + } + } + test(s"Foldable[$name].sliding3 consistent with List#sliding(3)") { + forAll { (fi: F[Int]) => + val n = 3 + (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding3) { case x1 :: x2 :: x3 :: Nil => + (x1, x2, x3) + } + } + } + test(s"Foldable[$name].sliding4 consistent with List#sliding(4)") { + forAll { (fi: F[Int]) => + val n = 4 + (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding4) { case x1 :: x2 :: x3 :: x4 :: Nil => + (x1, x2, x3, x4) + } + } + } + test(s"Foldable[$name].sliding5 consistent with List#sliding(5)") { + forAll { (fi: F[Int]) => + val n = 5 + (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding5) { + case x1 :: x2 :: x3 :: x4 :: x5 :: Nil => (x1, x2, x3, x4, x5) + } + } + } + test(s"Foldable[$name].sliding6 consistent with List#sliding(6)") { + forAll { (fi: F[Int]) => + val n = 6 + (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding6) { + case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: Nil => (x1, x2, x3, x4, x5, x6) + } + } + } + test(s"Foldable[$name].sliding7 consistent with List#sliding(7)") { + forAll { (fi: F[Int]) => + val n = 7 + (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding7) { + case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: Nil => (x1, x2, x3, x4, x5, x6, x7) + } + } + } + test(s"Foldable[$name].sliding8 consistent with List#sliding(8)") { + forAll { (fi: F[Int]) => + val n = 8 + (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding8) { + case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: x8 :: Nil => (x1, x2, x3, x4, x5, x6, x7, x8) + } + } + } + // skip sliding 10-22 as arbitrary collections of that size aren't generated + test(s"Foldable[$name].sliding9 consistent with List#sliding(9)") { + forAll { (fi: F[Int]) => + val n = 9 + (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding9) { + case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: x8 :: x9 :: Nil => (x1, x2, x3, x4, x5, x6, x7, x8, x9) + } + } + } + + def checkSlidingNConsistent[Tup <: Product: Eq](fi: F[Int], n: Int, slidingN: F[Int] => List[Tup])( + pf: PartialFunction[List[Int], Tup] + ) = { + val result = slidingN(fi) + val expected = fi.toList + .sliding(n) + .map(pf) + .toList + assert(result == expected) + } + } class FoldableSuiteAdditional extends CatsSuite with ScalaVersionSpecificFoldableSuite { From 66e2d430d9a3dbd2488872ca3859e35ab9f06f7d Mon Sep 17 00:00:00 2001 From: James Collier Date: Sun, 3 Jan 2021 21:59:17 +0000 Subject: [PATCH 06/11] fix scala 3 type issue --- .../test/scala/cats/tests/FoldableSuite.scala | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/tests/src/test/scala/cats/tests/FoldableSuite.scala b/tests/src/test/scala/cats/tests/FoldableSuite.scala index 25d921ca67..f31634e808 100644 --- a/tests/src/test/scala/cats/tests/FoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/FoldableSuite.scala @@ -285,7 +285,7 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit test(s"Foldable[$name].sliding2 consistent with List#sliding(2)") { forAll { (fi: F[Int]) => val n = 2 - (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding2) { case x1 :: x2 :: Nil => + checkSlidingNConsistent(fi, n, Foldable[F].sliding2) { case x1 :: x2 :: Nil => (x1, x2) } } @@ -293,7 +293,7 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit test(s"Foldable[$name].sliding3 consistent with List#sliding(3)") { forAll { (fi: F[Int]) => val n = 3 - (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding3) { case x1 :: x2 :: x3 :: Nil => + checkSlidingNConsistent(fi, n, Foldable[F].sliding3) { case x1 :: x2 :: x3 :: Nil => (x1, x2, x3) } } @@ -301,7 +301,7 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit test(s"Foldable[$name].sliding4 consistent with List#sliding(4)") { forAll { (fi: F[Int]) => val n = 4 - (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding4) { case x1 :: x2 :: x3 :: x4 :: Nil => + checkSlidingNConsistent(fi, n, Foldable[F].sliding4) { case x1 :: x2 :: x3 :: x4 :: Nil => (x1, x2, x3, x4) } } @@ -309,7 +309,7 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit test(s"Foldable[$name].sliding5 consistent with List#sliding(5)") { forAll { (fi: F[Int]) => val n = 5 - (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding5) { + checkSlidingNConsistent(fi, n, Foldable[F].sliding5) { case x1 :: x2 :: x3 :: x4 :: x5 :: Nil => (x1, x2, x3, x4, x5) } } @@ -317,7 +317,7 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit test(s"Foldable[$name].sliding6 consistent with List#sliding(6)") { forAll { (fi: F[Int]) => val n = 6 - (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding6) { + checkSlidingNConsistent(fi, n, Foldable[F].sliding6) { case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: Nil => (x1, x2, x3, x4, x5, x6) } } @@ -325,7 +325,7 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit test(s"Foldable[$name].sliding7 consistent with List#sliding(7)") { forAll { (fi: F[Int]) => val n = 7 - (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding7) { + checkSlidingNConsistent(fi, n, Foldable[F].sliding7) { case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: Nil => (x1, x2, x3, x4, x5, x6, x7) } } @@ -333,7 +333,7 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit test(s"Foldable[$name].sliding8 consistent with List#sliding(8)") { forAll { (fi: F[Int]) => val n = 8 - (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding8) { + checkSlidingNConsistent(fi, n, Foldable[F].sliding8) { case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: x8 :: Nil => (x1, x2, x3, x4, x5, x6, x7, x8) } } @@ -342,7 +342,7 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit test(s"Foldable[$name].sliding9 consistent with List#sliding(9)") { forAll { (fi: F[Int]) => val n = 9 - (n <= fi.size) ==> checkSlidingNConsistent(fi, n, Foldable[F].sliding9) { + checkSlidingNConsistent(fi, n, Foldable[F].sliding9) { case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: x8 :: x9 :: Nil => (x1, x2, x3, x4, x5, x6, x7, x8, x9) } } @@ -350,13 +350,15 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit def checkSlidingNConsistent[Tup <: Product: Eq](fi: F[Int], n: Int, slidingN: F[Int] => List[Tup])( pf: PartialFunction[List[Int], Tup] - ) = { - val result = slidingN(fi) - val expected = fi.toList - .sliding(n) - .map(pf) - .toList - assert(result == expected) + ): Unit = { + if (n <= fi.size) { + val result = slidingN(fi) + val expected = fi.toList + .sliding(n) + .map(pf) + .toList + assert(result === expected) + } } } From df4623089b7a5d540efd51bf8452e71fb53e41ee Mon Sep 17 00:00:00 2001 From: James Collier Date: Sun, 3 Jan 2021 22:30:14 +0000 Subject: [PATCH 07/11] add scaladoc for slidingn --- project/Boilerplate.scala | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index be13181f91..17f894adef 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -522,7 +522,24 @@ object Boilerplate { | * @groupprio Ungrouped 0 | * | * @groupname FoldableSlidingN foldable arity - | * @groupdesc FoldableSlidingN Sliding windows of size N + | * @groupdesc FoldableSlidingN + | * Group sequential elements into fixed sized tuples by passing a "sliding window" over them. + | * + | * A foldable with fewer elements than the window size will return an empty list unlike `Iterable#sliding(size: Int)`. + | * Example: + | * {{{ + | * import cats.Foldable + | * scala> Foldable[List].sliding2((1 to 10).toList) + | * val res0: List[(Int, Int)] = List((1,2), (2,3), (3,4), (4,5), (5,6), (6,7), (7,8), (8,9), (9,10)) + | * + | * scala> Foldable[List].sliding4((1 to 10).toList) + | * val res1: List[(Int, Int, Int, Int)] = List((1,2,3,4), (2,3,4,5), (3,4,5,6), (4,5,6,7), (5,6,7,8), (6,7,8,9), (7,8,9,10)) + | * + | * scala> Foldable[List].sliding4((1 to 2).toList) + | * val res2: List[(Int, Int, Int, Int)] = List() + | * + | * }}} + | * | * @groupprio FoldableSlidingN 999 | * | */ From be8eda82070114fe57880a00ea3919365d8237f0 Mon Sep 17 00:00:00 2001 From: James Collier Date: Sun, 3 Jan 2021 22:41:04 +0000 Subject: [PATCH 08/11] fmt --- .../test/scala/cats/tests/FoldableSuite.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/src/test/scala/cats/tests/FoldableSuite.scala b/tests/src/test/scala/cats/tests/FoldableSuite.scala index f31634e808..7bb0959975 100644 --- a/tests/src/test/scala/cats/tests/FoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/FoldableSuite.scala @@ -309,32 +309,32 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit test(s"Foldable[$name].sliding5 consistent with List#sliding(5)") { forAll { (fi: F[Int]) => val n = 5 - checkSlidingNConsistent(fi, n, Foldable[F].sliding5) { - case x1 :: x2 :: x3 :: x4 :: x5 :: Nil => (x1, x2, x3, x4, x5) + checkSlidingNConsistent(fi, n, Foldable[F].sliding5) { case x1 :: x2 :: x3 :: x4 :: x5 :: Nil => + (x1, x2, x3, x4, x5) } } } test(s"Foldable[$name].sliding6 consistent with List#sliding(6)") { forAll { (fi: F[Int]) => val n = 6 - checkSlidingNConsistent(fi, n, Foldable[F].sliding6) { - case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: Nil => (x1, x2, x3, x4, x5, x6) + checkSlidingNConsistent(fi, n, Foldable[F].sliding6) { case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: Nil => + (x1, x2, x3, x4, x5, x6) } } } test(s"Foldable[$name].sliding7 consistent with List#sliding(7)") { forAll { (fi: F[Int]) => val n = 7 - checkSlidingNConsistent(fi, n, Foldable[F].sliding7) { - case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: Nil => (x1, x2, x3, x4, x5, x6, x7) + checkSlidingNConsistent(fi, n, Foldable[F].sliding7) { case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: Nil => + (x1, x2, x3, x4, x5, x6, x7) } } } test(s"Foldable[$name].sliding8 consistent with List#sliding(8)") { forAll { (fi: F[Int]) => val n = 8 - checkSlidingNConsistent(fi, n, Foldable[F].sliding8) { - case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: x8 :: Nil => (x1, x2, x3, x4, x5, x6, x7, x8) + checkSlidingNConsistent(fi, n, Foldable[F].sliding8) { case x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: x8 :: Nil => + (x1, x2, x3, x4, x5, x6, x7, x8) } } } From a5bfd36c368de776358f1bb54bd514ddcfb9eb0e Mon Sep 17 00:00:00 2001 From: James Collier Date: Sun, 3 Jan 2021 22:53:01 +0000 Subject: [PATCH 09/11] add slidingn syntax --- .../src/main/scala/cats/syntax/foldable.scala | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/src/main/scala/cats/syntax/foldable.scala b/core/src/main/scala/cats/syntax/foldable.scala index 78fed4ee7c..9bd3c351fa 100644 --- a/core/src/main/scala/cats/syntax/foldable.scala +++ b/core/src/main/scala/cats/syntax/foldable.scala @@ -283,6 +283,32 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal { f: A => G[Either[B, C]] )(implicit A: Alternative[F], F: Foldable[F], M: Monad[G]): G[(F[B], F[C])] = F.partitionEitherM[G, A, B, C](fa)(f)(A, M) + + def sliding2(implicit F: Foldable[F]): List[(A, A)] = F.sliding2(fa) + def sliding3(implicit F: Foldable[F]): List[(A, A, A)] = F.sliding3(fa) + def sliding4(implicit F: Foldable[F]): List[(A, A, A, A)] = F.sliding4(fa) + def sliding5(implicit F: Foldable[F]): List[(A, A, A, A, A)] = F.sliding5(fa) + def sliding6(implicit F: Foldable[F]): List[(A, A, A, A, A, A)] = F.sliding6(fa) + def sliding7(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A)] = F.sliding7(fa) + def sliding8(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A)] = F.sliding8(fa) + def sliding9(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A)] = F.sliding9(fa) + def sliding10(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A)] = F.sliding10(fa) + def sliding11(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A)] = F.sliding11(fa) + def sliding12(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding12(fa) + def sliding13(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding13(fa) + def sliding14(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding14(fa) + def sliding15(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding15(fa) + def sliding16(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding16(fa) + def sliding17(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding17(fa) + def sliding18(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding18(fa) + def sliding19(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = + F.sliding19(fa) + def sliding20(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = + F.sliding20(fa) + def sliding21(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = + F.sliding21(fa) + def sliding22(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = + F.sliding22(fa) } @deprecated("Use methods on Foldable", "2.1.0-RC1") From 305d5ba84dae304348abe1a9e8ead15d8d2c1570 Mon Sep 17 00:00:00 2001 From: James Collier Date: Sun, 3 Jan 2021 22:57:14 +0000 Subject: [PATCH 10/11] test empty when size < n --- tests/src/test/scala/cats/tests/FoldableSuite.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/src/test/scala/cats/tests/FoldableSuite.scala b/tests/src/test/scala/cats/tests/FoldableSuite.scala index 7bb0959975..1c4461c110 100644 --- a/tests/src/test/scala/cats/tests/FoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/FoldableSuite.scala @@ -351,13 +351,15 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit def checkSlidingNConsistent[Tup <: Product: Eq](fi: F[Int], n: Int, slidingN: F[Int] => List[Tup])( pf: PartialFunction[List[Int], Tup] ): Unit = { + val result = slidingN(fi) if (n <= fi.size) { - val result = slidingN(fi) val expected = fi.toList .sliding(n) .map(pf) .toList assert(result === expected) + } else { + assert(result.isEmpty) } } From ae338240e493bd95eaae5f844895c7236e90f463 Mon Sep 17 00:00:00 2001 From: James Collier Date: Sun, 3 Jan 2021 23:05:00 +0000 Subject: [PATCH 11/11] rename FoldableArityFunctions => FoldableNFunctions --- core/src/main/scala/cats/Foldable.scala | 2 +- project/Boilerplate.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index 45fb82ddf6..6adf30ddb3 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -30,7 +30,7 @@ import scala.annotation.implicitNotFound * See: [[http://www.cs.nott.ac.uk/~pszgmh/fold.pdf A tutorial on the universality and expressiveness of fold]] */ @implicitNotFound("Could not find an instance of Foldable for ${F}") -@typeclass trait Foldable[F[_]] extends UnorderedFoldable[F] with FoldableArityFunctions[F] { self => +@typeclass trait Foldable[F[_]] extends UnorderedFoldable[F] with FoldableNFunctions[F] { self => /** * Left associative fold on 'F' using the function 'f'. diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index 17f894adef..e2100d1f50 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -505,7 +505,7 @@ object Boilerplate { } object GenFoldableArityFunctions extends Template { - def filename(root: File) = root / "cats" / "FoldableArityFunctions.scala" + def filename(root: File) = root / "cats" / "FoldableNFunctions.scala" override def range = 2 to maxArity def content(tv: TemplateVals) = { import tv._ @@ -543,7 +543,7 @@ object Boilerplate { | * @groupprio FoldableSlidingN 999 | * | */ - |trait FoldableArityFunctions[F[_]] { self: Foldable[F] => + |trait FoldableNFunctions[F[_]] { self: Foldable[F] => - /** @group FoldableSlidingN */ - def sliding$arity[A](fa: F[A]): List[$tupleTpe] = - foldRight(fa, Now((List.empty[$tupleTpe], List.empty[A]))) { (x1, eval) =>