Skip to content

Commit a7fd543

Browse files
authored
Avoid all evaluation of LazyList#foldRightDefer (#3567)
1 parent 330ac25 commit a7fd543

File tree

2 files changed

+22
-6
lines changed

2 files changed

+22
-6
lines changed

core/src/main/scala/cats/Foldable.scala

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,15 @@ import scala.annotation.implicitNotFound
9797
*/
9898
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B]
9999

100-
def foldRightDefer[G[_]: Defer, A, B](fa: F[A], gb: G[B])(fn: (A, G[B]) => G[B]): G[B] =
101-
Defer[G].defer(
102-
foldLeft(fa, (z: G[B]) => z) { (acc, elem) => z =>
103-
Defer[G].defer(acc(fn(elem, z)))
104-
}(gb)
105-
)
100+
def foldRightDefer[G[_]: Defer, A, B](fa: F[A], gb: G[B])(fn: (A, G[B]) => G[B]): G[B] = {
101+
def loop(source: Source[A]): G[B] = {
102+
source.uncons match {
103+
case Some((next, s)) => fn(next, Defer[G].defer(loop(s.value)))
104+
case None => gb
105+
}
106+
}
107+
Defer[G].defer(loop(Source.fromFoldable(fa)(self)))
108+
}
106109

107110
def reduceLeftToOption[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): Option[B] =
108111
foldLeft(fa, Option.empty[B]) {

tests/src/test/scala-2.13+/cats/tests/LazyListSuite.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ import cats.laws.discipline.{
1717
import cats.laws.discipline.arbitrary._
1818
import cats.syntax.show._
1919
import cats.syntax.eq._
20+
import cats.syntax.foldable._
2021
import org.scalacheck.Prop._
2122

23+
import scala.util.control.TailCalls
24+
2225
class LazyListSuite extends CatsSuite {
2326
checkAll("LazyList[Int]", SemigroupalTests[LazyList].semigroupal[Int, Int, Int])
2427
checkAll("Semigroupal[LazyList]", SerializableTests.serializable(Semigroupal[LazyList]))
@@ -52,6 +55,16 @@ class LazyListSuite extends CatsSuite {
5255
assert(LazyList.empty[Int].show === (s"LazyList()"))
5356
}
5457

58+
test("Avoid all evaluation of LazyList#foldRightDefer") {
59+
val sum = LazyList
60+
.from(1)
61+
.foldRightDefer(TailCalls.done(0)) { (elem, acc) =>
62+
if (elem <= 100) acc.map(_ + elem) else TailCalls.done(0)
63+
}
64+
.result
65+
(1 to 100).sum === sum
66+
}
67+
5568
test("Show[LazyList] is referentially transparent, unlike LazyList.toString") {
5669
forAll { (lazyList: LazyList[Int]) =>
5770
if (!lazyList.isEmpty) {

0 commit comments

Comments
 (0)