Skip to content

Commit

Permalink
Avoid all evaluation of LazyList#foldRightDefer (#3567)
Browse files Browse the repository at this point in the history
  • Loading branch information
takayahilton authored Aug 15, 2020
1 parent 330ac25 commit a7fd543
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 6 deletions.
15 changes: 9 additions & 6 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,15 @@ import scala.annotation.implicitNotFound
*/
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B]

def foldRightDefer[G[_]: Defer, A, B](fa: F[A], gb: G[B])(fn: (A, G[B]) => G[B]): G[B] =
Defer[G].defer(
foldLeft(fa, (z: G[B]) => z) { (acc, elem) => z =>
Defer[G].defer(acc(fn(elem, z)))
}(gb)
)
def foldRightDefer[G[_]: Defer, A, B](fa: F[A], gb: G[B])(fn: (A, G[B]) => G[B]): G[B] = {
def loop(source: Source[A]): G[B] = {
source.uncons match {
case Some((next, s)) => fn(next, Defer[G].defer(loop(s.value)))
case None => gb
}
}
Defer[G].defer(loop(Source.fromFoldable(fa)(self)))
}

def reduceLeftToOption[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): Option[B] =
foldLeft(fa, Option.empty[B]) {
Expand Down
13 changes: 13 additions & 0 deletions tests/src/test/scala-2.13+/cats/tests/LazyListSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ import cats.laws.discipline.{
import cats.laws.discipline.arbitrary._
import cats.syntax.show._
import cats.syntax.eq._
import cats.syntax.foldable._
import org.scalacheck.Prop._

import scala.util.control.TailCalls

class LazyListSuite extends CatsSuite {
checkAll("LazyList[Int]", SemigroupalTests[LazyList].semigroupal[Int, Int, Int])
checkAll("Semigroupal[LazyList]", SerializableTests.serializable(Semigroupal[LazyList]))
Expand Down Expand Up @@ -52,6 +55,16 @@ class LazyListSuite extends CatsSuite {
assert(LazyList.empty[Int].show === (s"LazyList()"))
}

test("Avoid all evaluation of LazyList#foldRightDefer") {
val sum = LazyList
.from(1)
.foldRightDefer(TailCalls.done(0)) { (elem, acc) =>
if (elem <= 100) acc.map(_ + elem) else TailCalls.done(0)
}
.result
(1 to 100).sum === sum
}

test("Show[LazyList] is referentially transparent, unlike LazyList.toString") {
forAll { (lazyList: LazyList[Int]) =>
if (!lazyList.isEmpty) {
Expand Down

0 comments on commit a7fd543

Please sign in to comment.