From 2b8185d6f2d5739a7251249a2e1a945c6c4c1ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Pr=C3=A9centh?= Date: Fri, 3 May 2024 15:50:48 +0200 Subject: [PATCH 1/2] Add scanLeft to NonEmptyCollection --- .../main/scala-2.13+/cats/data/NonEmptyLazyList.scala | 3 +++ core/src/main/scala/cats/data/NonEmptyChain.scala | 11 +++++++++++ .../src/main/scala/cats/data/NonEmptyCollection.scala | 1 + core/src/main/scala/cats/data/NonEmptyList.scala | 3 +++ core/src/main/scala/cats/data/NonEmptySeq.scala | 3 +++ core/src/main/scala/cats/data/NonEmptyVector.scala | 3 +++ 6 files changed, 24 insertions(+) diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala index 846b960ef1..b6d42199c3 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -274,6 +274,9 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) final def foldRight[B](z: B)(f: (A, B) => B): B = toLazyList.foldRight(z)(f) + final def scanLeft[B](b: B)(f: (B, A) => B): NonEmptyLazyList[B] = + create(toLazyList.scanLeft(b)(f)) + /** * Left-associative reduce using f. */ diff --git a/core/src/main/scala/cats/data/NonEmptyChain.scala b/core/src/main/scala/cats/data/NonEmptyChain.scala index 3742a92268..ef2e81aeef 100644 --- a/core/src/main/scala/cats/data/NonEmptyChain.scala +++ b/core/src/main/scala/cats/data/NonEmptyChain.scala @@ -302,6 +302,17 @@ class NonEmptyChainOps[A](private val value: NonEmptyChain[A]) final def foldLeft[B](b: B)(f: (B, A) => B): B = toChain.foldLeft(b)(f) + final def scanLeft[B](b: B)(f: (B, A) => B): NonEmptyChain[B] = { + var current = b + var result = Chain.one[B](b) + val iter = toChain.iterator + while (iter.hasNext) { + current = f(current, iter.next()) + result = result :+ current + } + create(result) + } + /** * Right-associative fold using f. */ diff --git a/core/src/main/scala/cats/data/NonEmptyCollection.scala b/core/src/main/scala/cats/data/NonEmptyCollection.scala index 72c158a84e..52d24153f0 100644 --- a/core/src/main/scala/cats/data/NonEmptyCollection.scala +++ b/core/src/main/scala/cats/data/NonEmptyCollection.scala @@ -46,6 +46,7 @@ private[cats] trait NonEmptyCollection[+A, U[+_], NE[+_]] extends Any { def forall(p: A => Boolean): Boolean def foldLeft[B](b: B)(f: (B, A) => B): B + def scanLeft[B](b: B)(f: (B, A) => B): NE[B] def reduce[AA >: A](implicit S: Semigroup[AA]): AA def zipWith[B, C](b: NE[B])(f: (A, B) => C): NE[C] diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index c2e86caa60..b66b0693d8 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -302,6 +302,9 @@ final case class NonEmptyList[+A](head: A, tail: List[A]) extends NonEmptyCollec def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = Foldable[List].foldRight(toList, lb)(f) + def scanLeft[B](b: B)(f: (B, A) => B): NonEmptyList[B] = + NonEmptyList(b, tail.scanLeft(f(b, head))(f)) + /** * Left-associative reduce using f. */ diff --git a/core/src/main/scala/cats/data/NonEmptySeq.scala b/core/src/main/scala/cats/data/NonEmptySeq.scala index f43ef75ac4..1ce2991b72 100644 --- a/core/src/main/scala/cats/data/NonEmptySeq.scala +++ b/core/src/main/scala/cats/data/NonEmptySeq.scala @@ -178,6 +178,9 @@ final class NonEmptySeq[+A] private (val toSeq: Seq[A]) extends AnyVal with NonE def foldLeft[B](b: B)(f: (B, A) => B): B = toSeq.foldLeft(b)(f) + def scanLeft[B](b: B)(f: (B, A) => B): NonEmptySeq[B] = + new NonEmptySeq(toSeq.scanLeft(b)(f)) + /** * Right-associative fold using f. */ diff --git a/core/src/main/scala/cats/data/NonEmptyVector.scala b/core/src/main/scala/cats/data/NonEmptyVector.scala index 583dd63612..aef6e877ea 100644 --- a/core/src/main/scala/cats/data/NonEmptyVector.scala +++ b/core/src/main/scala/cats/data/NonEmptyVector.scala @@ -188,6 +188,9 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A]) def foldLeft[B](b: B)(f: (B, A) => B): B = toVector.foldLeft(b)(f) + def scanLeft[B](b: B)(f: (B, A) => B): NonEmptyVector[B] = + NonEmptyVector.fromVectorUnsafe(toVector.scanLeft(b)(f)) + /** * Right-associative fold using f. */ From 3b18c23a726062a78ff15569c35227ad97501860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Pr=C3=A9centh?= Date: Fri, 3 May 2024 15:51:03 +0200 Subject: [PATCH 2/2] Add scanLeftTail to NonEmptyCollection. Considering doing a scan on a non-empty collection yields a collection with at least _two_ elements, this method helps users discover that fact. --- core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala | 3 +++ core/src/main/scala/cats/data/NonEmptyChain.scala | 3 +++ core/src/main/scala/cats/data/NonEmptyCollection.scala | 1 + core/src/main/scala/cats/data/NonEmptyList.scala | 3 +++ core/src/main/scala/cats/data/NonEmptySeq.scala | 3 +++ core/src/main/scala/cats/data/NonEmptyVector.scala | 3 +++ 6 files changed, 16 insertions(+) diff --git a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala index b6d42199c3..5a99712bb6 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -277,6 +277,9 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) final def scanLeft[B](b: B)(f: (B, A) => B): NonEmptyLazyList[B] = create(toLazyList.scanLeft(b)(f)) + final def scanLeftTail[B](b: B)(f: (B, A) => B): NonEmptyLazyList[B] = + create(toLazyList.scanLeft(b)(f).tail) + /** * Left-associative reduce using f. */ diff --git a/core/src/main/scala/cats/data/NonEmptyChain.scala b/core/src/main/scala/cats/data/NonEmptyChain.scala index ef2e81aeef..af0d985e17 100644 --- a/core/src/main/scala/cats/data/NonEmptyChain.scala +++ b/core/src/main/scala/cats/data/NonEmptyChain.scala @@ -313,6 +313,9 @@ class NonEmptyChainOps[A](private val value: NonEmptyChain[A]) create(result) } + final def scanLeftTail[B](b: B)(f: (B, A) => B): NonEmptyChain[B] = + create(scanLeft(b)(f).tail) + /** * Right-associative fold using f. */ diff --git a/core/src/main/scala/cats/data/NonEmptyCollection.scala b/core/src/main/scala/cats/data/NonEmptyCollection.scala index 52d24153f0..c3601d962d 100644 --- a/core/src/main/scala/cats/data/NonEmptyCollection.scala +++ b/core/src/main/scala/cats/data/NonEmptyCollection.scala @@ -47,6 +47,7 @@ private[cats] trait NonEmptyCollection[+A, U[+_], NE[+_]] extends Any { def foldLeft[B](b: B)(f: (B, A) => B): B def scanLeft[B](b: B)(f: (B, A) => B): NE[B] + def scanLeftTail[B](b: B)(f: (B, A) => B): NE[B] def reduce[AA >: A](implicit S: Semigroup[AA]): AA def zipWith[B, C](b: NE[B])(f: (A, B) => C): NE[C] diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index b66b0693d8..097afcd219 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -305,6 +305,9 @@ final case class NonEmptyList[+A](head: A, tail: List[A]) extends NonEmptyCollec def scanLeft[B](b: B)(f: (B, A) => B): NonEmptyList[B] = NonEmptyList(b, tail.scanLeft(f(b, head))(f)) + def scanLeftTail[B](b: B)(f: (B, A) => B): NonEmptyList[B] = + NonEmptyList.fromListUnsafe(tail.scanLeft(f(b, head))(f)) + /** * Left-associative reduce using f. */ diff --git a/core/src/main/scala/cats/data/NonEmptySeq.scala b/core/src/main/scala/cats/data/NonEmptySeq.scala index 1ce2991b72..9ec273b1bb 100644 --- a/core/src/main/scala/cats/data/NonEmptySeq.scala +++ b/core/src/main/scala/cats/data/NonEmptySeq.scala @@ -181,6 +181,9 @@ final class NonEmptySeq[+A] private (val toSeq: Seq[A]) extends AnyVal with NonE def scanLeft[B](b: B)(f: (B, A) => B): NonEmptySeq[B] = new NonEmptySeq(toSeq.scanLeft(b)(f)) + def scanLeftTail[B](b: B)(f: (B, A) => B): NonEmptySeq[B] = + new NonEmptySeq(toSeq.scanLeft(b)(f).tail) + /** * Right-associative fold using f. */ diff --git a/core/src/main/scala/cats/data/NonEmptyVector.scala b/core/src/main/scala/cats/data/NonEmptyVector.scala index aef6e877ea..ff9fabc2e2 100644 --- a/core/src/main/scala/cats/data/NonEmptyVector.scala +++ b/core/src/main/scala/cats/data/NonEmptyVector.scala @@ -191,6 +191,9 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A]) def scanLeft[B](b: B)(f: (B, A) => B): NonEmptyVector[B] = NonEmptyVector.fromVectorUnsafe(toVector.scanLeft(b)(f)) + def scanLeftTail[B](b: B)(f: (B, A) => B): NonEmptyVector[B] = + NonEmptyVector.fromVectorUnsafe(tail.scanLeft(f(b, head))(f)) + /** * Right-associative fold using f. */