From 4939f83485c2f476aef7ba7cb8872aed2c49c707 Mon Sep 17 00:00:00 2001 From: takayahilton Date: Tue, 18 Aug 2020 22:18:44 +0900 Subject: [PATCH] Avoid all evaluation of NonEmptyLazyList#reduceRightTo --- .../scala-2.13+/cats/data/NonEmptyLazyList.scala | 9 ++++++--- core/src/main/scala/cats/instances/function.scala | 8 +++----- .../cats/tests/NonEmptyLazyListSuite.scala | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 8 deletions(-) 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 2e2b64d088..71e8a87a8d 100644 --- a/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala +++ b/core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala @@ -484,9 +484,12 @@ sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLa def reduceLeftTo[A, B](fa: NonEmptyLazyList[A])(f: A => B)(g: (B, A) => B): B = fa.reduceLeftTo(f)(g) def reduceRightTo[A, B](fa: NonEmptyLazyList[A])(f: A => B)(g: (A, cats.Eval[B]) => cats.Eval[B]): cats.Eval[B] = - Eval.defer(fa.reduceRightTo(a => Eval.later(f(a))) { (a, b) => - Eval.defer(g(a, b)) - }) + fa.tail match { + case LazyList() => Eval.later(f(fa.head)) + case head +: tail => + val nell = NonEmptyLazyList.fromLazyListPrepend(head, tail) + g(fa.head, Eval.defer(reduceRightTo(nell)(f)(g))) + } private val alignInstance = Align[LazyList].asInstanceOf[Align[NonEmptyLazyList]] diff --git a/core/src/main/scala/cats/instances/function.scala b/core/src/main/scala/cats/instances/function.scala index feed2f8b4a..8221821bd8 100644 --- a/core/src/main/scala/cats/instances/function.scala +++ b/core/src/main/scala/cats/instances/function.scala @@ -117,11 +117,9 @@ sealed private[instances] trait Function1Instances extends Function1Instances0 { def unit: Unit => R = Function.const(Monoid[R].empty) def contramap[A, B](fa: A => R)(f: B => A): B => R = fa.compose(f) - def product[A, B](fa: A => R, fb: B => R): ((A, B)) => R = - (ab: (A, B)) => - ab match { - case (a, b) => Monoid[R].combine(fa(a), fb(b)) - } + def product[A, B](fa: A => R, fb: B => R): ((A, B)) => R = { + case (a, b) => Monoid[R].combine(fa(a), fb(b)) + } } implicit def catsStdMonadForFunction1[T1]: Monad[T1 => *] = diff --git a/tests/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala b/tests/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala index 9c0459f4c7..1957e3a1d8 100644 --- a/tests/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala +++ b/tests/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala @@ -16,6 +16,8 @@ import cats.laws.discipline.arbitrary._ import cats.syntax.either._ import cats.syntax.foldable._ import cats.syntax.eq._ +import cats.Reducible +import cats.Eval import org.scalacheck.Prop._ class NonEmptyLazyListSuite extends NonEmptyCollectionSuite[LazyList, NonEmptyLazyList, NonEmptyLazyListOps] { @@ -164,6 +166,19 @@ class NonEmptyLazyListSuite extends NonEmptyCollectionSuite[LazyList, NonEmptyLa assert(ci.toNev === (NonEmptyVector.fromVectorUnsafe(Vector.empty[Int] ++ ci.toList.toVector))) } } + + test("Avoid all evaluation of NonEmptyLazyList#reduceRightTo") { + val sum = implicitly[Reducible[NonEmptyLazyList]] + .reduceRightTo( + NonEmptyLazyList + .fromLazyListPrepend(1, LazyList.from(2)) + )(identity) { (elem, acc) => + if (elem <= 100) acc.map(_ + elem) else Eval.later(0) + } + .value + + (1 to 100).sum === sum + } } class ReducibleNonEmptyLazyListSuite extends ReducibleSuite[NonEmptyLazyList]("NonEmptyLazyList") {