diff --git a/free/src/main/scala/cats/free/Cofree.scala b/free/src/main/scala/cats/free/Cofree.scala index f56ef00885..b2e9e95e44 100644 --- a/free/src/main/scala/cats/free/Cofree.scala +++ b/free/src/main/scala/cats/free/Cofree.scala @@ -80,7 +80,7 @@ object Cofree extends CofreeInstances { * A stack-safe algebraic recursive fold out of the cofree comonad. */ def cata[F[_], A, B](cof: Cofree[F, A])(folder: (A, F[B]) => Eval[B])(implicit F: Traverse[F]): Eval[B] = - F.traverse(cof.tailForced)(cata(_)(folder)).flatMap(folder(cof.head, _)) + F.traverse(cof.tailForced)(c => Eval.defer(cata(c)(folder))).flatMap(folder(cof.head, _)) /** * A monadic recursive fold out of the cofree comonad into a monad which can express Eval's stack-safety. diff --git a/free/src/test/scala/cats/free/CofreeSuite.scala b/free/src/test/scala/cats/free/CofreeSuite.scala index e54db3ff7b..eb2f7810a9 100644 --- a/free/src/test/scala/cats/free/CofreeSuite.scala +++ b/free/src/test/scala/cats/free/CofreeSuite.scala @@ -108,6 +108,19 @@ class CofreeSuite extends CatsSuite { cata should ===(nelUnfoldedHundred) } + test("Cofree.cata is stack-safe") { + val unfolded = Cofree.unfold[Option, Int](0)(i => if (i == 50000) None else Some(i + 1)) + val sum = List.tabulate(50000)(identity).sum + val cata = + Cofree + .cata[Option, Int, Int](unfolded)( + (i, lb) => Eval.now(lb.fold(0)(_ + i)) + ) + .value + + cata should ===(sum) + } + test("Cofree.cataM") { type EvalOption[A] = OptionT[Eval, A]