diff --git a/core/src/main/scala/cats/Eval.scala b/core/src/main/scala/cats/Eval.scala index 934e6d12372..4779eb2023f 100644 --- a/core/src/main/scala/cats/Eval.scala +++ b/core/src/main/scala/cats/Eval.scala @@ -301,7 +301,10 @@ private[cats] trait EvalInstances extends EvalInstances0 { def flatMap[A, B](fa: Eval[A])(f: A => Eval[B]): Eval[B] = fa.flatMap(f) def extract[A](la: Eval[A]): A = la.value def coflatMap[A, B](fa: Eval[A])(f: Eval[A] => B): Eval[B] = Later(f(fa)) - def tailRecM[A, B](a: A)(f: A => Eval[Either[A, B]]): Eval[B] = defaultTailRecM(a)(f) + def tailRecM[A, B](a: A)(f: A => Eval[Either[A, B]]): Eval[B] = f(a).flatMap { // OK because Eval is trampolined + case Left(nextA) => tailRecM(nextA)(f) + case Right(b) => pure(b) + } } implicit def catsOrderForEval[A: Order]: Order[Eval[A]] = diff --git a/core/src/main/scala/cats/Monad.scala b/core/src/main/scala/cats/Monad.scala index b8388fa7b57..21e2d6d7bfb 100644 --- a/core/src/main/scala/cats/Monad.scala +++ b/core/src/main/scala/cats/Monad.scala @@ -14,16 +14,4 @@ import simulacrum.typeclass @typeclass trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { override def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(a => pure(f(a))) - - /** - * This is not stack safe if the monad is not trampolined, but it - * is always lawful. It it better if you can find a stack safe way - * to write this method (all cats types have a stack safe version - * of this). When this method is safe you can find an `implicit r: RecursiveTailRecM`. - */ - protected def defaultTailRecM[A, B](a: A)(fn: A => F[Either[A, B]]): F[B] = - flatMap(fn(a)) { - case Right(b) => pure(b) - case Left(nextA) => defaultTailRecM(nextA)(fn) - } } diff --git a/free/src/main/scala/cats/free/Free.scala b/free/src/main/scala/cats/free/Free.scala index 0634ac87b7f..f16f5333595 100644 --- a/free/src/main/scala/cats/free/Free.scala +++ b/free/src/main/scala/cats/free/Free.scala @@ -127,14 +127,6 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { case FlatMapped(c, g) => M.map(c.foldMap(f))(cc => Left(g(cc))) }) - /** - * Same as foldMap but without a guarantee of stack safety. If the recursion is shallow - * enough, this will work - */ - final def foldMapUnsafe[M[_]](f: FunctionK[S, M])(implicit M: Monad[M]): M[A] = - foldMap[M](f) - - /** * Compile your free monad into another language by changing the * suspension functor using the given natural transformation `f`. @@ -143,7 +135,7 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { * effects will be applied by `compile`. */ final def compile[T[_]](f: FunctionK[S, T]): Free[T, A] = - foldMapUnsafe[Free[T, ?]] { // this is safe because Free is stack safe + foldMap[Free[T, ?]] { // this is safe because Free is stack safe new FunctionK[S, Free[T, ?]] { def apply[B](fa: S[B]): Free[T, B] = Suspend(f(fa)) } diff --git a/laws/src/main/scala/cats/laws/discipline/MonadTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadTests.scala index 42741bae5d7..50d355125b6 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadTests.scala @@ -2,6 +2,7 @@ package cats package laws package discipline +import catalysts.Platform import cats.laws.discipline.CartesianTests.Isomorphisms import org.scalacheck.Arbitrary import org.scalacheck.Prop @@ -30,9 +31,8 @@ trait MonadTests[F[_]] extends ApplicativeTests[F] with FlatMapTests[F] { def props: Seq[(String, Prop)] = Seq( "monad left identity" -> forAll(laws.monadLeftIdentity[A, B] _), "monad right identity" -> forAll(laws.monadRightIdentity[A] _), - "map flatMap coherence" -> forAll(laws.mapFlatMapCoherence[A, B] _), - "tailRecM stack safety" -> laws.tailRecMStackSafety - ) + "map flatMap coherence" -> forAll(laws.mapFlatMapCoherence[A, B] _) + ) ++ (if (Platform.isJvm) Seq[(String, Prop)]("tailRecM stack safety" -> laws.tailRecMStackSafety) else Seq.empty) } } }