From 8d7a795e1e3200cdf8d1df492337527df963150a Mon Sep 17 00:00:00 2001 From: satorg Date: Tue, 5 May 2026 23:42:27 -0700 Subject: [PATCH] re-use `Apply.map2Eval` implementation in `FlatMap` --- core/src/main/scala/cats/FlatMap.scala | 6 ++++-- .../test/scala/cats/tests/OptionTSuite.scala | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/cats/FlatMap.scala b/core/src/main/scala/cats/FlatMap.scala index cd1b4356ce..0249d3cd8d 100644 --- a/core/src/main/scala/cats/FlatMap.scala +++ b/core/src/main/scala/cats/FlatMap.scala @@ -113,8 +113,10 @@ trait FlatMap[F[_]] extends Apply[F] with FlatMapArityFunctions[F] { override def map2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z): F[Z] = flatMap(fa)(a => map(fb)(b => f(a, b))) - override def map2Eval[A, B, Z](fa: F[A], fb: Eval[F[B]])(f: (A, B) => Z): Eval[F[Z]] = - Eval.now(flatMap(fa)(a => map(fb.value)(b => f(a, b)))) + override def map2Eval[A, B, Z](fa: F[A], efb: Eval[F[B]])(f: (A, B) => Z): Eval[F[Z]] = + // Can't simply call `super.map2Eval(...)` because of MiMa errors. + // Neither can simly remove this override for the same reason. + efb.map(fb => map2(fa, fb)(f)) override def productR[A, B](fa: F[A])(fb: F[B]): F[B] = flatMap(fa)(_ => fb) diff --git a/tests/shared/src/test/scala/cats/tests/OptionTSuite.scala b/tests/shared/src/test/scala/cats/tests/OptionTSuite.scala index 1fe21fa7ea..e9ec8800e2 100644 --- a/tests/shared/src/test/scala/cats/tests/OptionTSuite.scala +++ b/tests/shared/src/test/scala/cats/tests/OptionTSuite.scala @@ -25,15 +25,16 @@ import cats.* import cats.data.{Const, IdT, OptionT, State} import cats.kernel.laws.discipline.{EqTests, MonoidTests, OrderTests, PartialOrderTests, SemigroupTests} import cats.laws.discipline.* +import cats.laws.discipline.SemigroupalTests.Isomorphisms import cats.laws.discipline.arbitrary.* import cats.laws.discipline.eq.* -import cats.laws.discipline.SemigroupalTests.Isomorphisms import cats.syntax.applicative.* +import cats.syntax.apply.* import cats.syntax.either.* +import cats.syntax.eq.* import cats.syntax.flatMap.* import cats.syntax.functor.* import cats.syntax.monadError.* -import cats.syntax.eq.* import org.scalacheck.Prop.* class OptionTSuite extends CatsSuite { @@ -583,6 +584,19 @@ class OptionTSuite extends CatsSuite { } } + test("Apply.map2Eval is stack safe for OptionT via Foldable.foldRight") { + val G = Applicative[OptionT[Id, *]] + val obtained = + Foldable[List] + .foldRight(List.range(0, 12345), Eval.now(OptionT.pure[Id](0L))) { (a, accEvalG) => + G.pure(a).map2Eval(accEvalG) { _ + _ } + } + .value + .value + + assertEquals(obtained, Some(76193340L)) + } + /** * Testing that implicit resolution works. If it compiles, the "test" passes. */