diff --git a/core/src/main/scala/cats/std/try.scala b/core/src/main/scala/cats/std/try.scala index d10d919fd8..3cdfc607cb 100644 --- a/core/src/main/scala/cats/std/try.scala +++ b/core/src/main/scala/cats/std/try.scala @@ -6,12 +6,13 @@ import TryInstances.castFailure import scala.util.control.NonFatal import scala.util.{Failure, Success, Try} +import scala.annotation.tailrec trait TryInstances extends TryInstances1 { // scalastyle:off method.length - implicit def catsStdInstancesForTry: MonadError[Try, Throwable] with CoflatMap[Try] with Traverse[Try] = - new TryCoflatMap with MonadError[Try, Throwable] with Traverse[Try] { + implicit def catsStdInstancesForTry: MonadError[Try, Throwable] with CoflatMap[Try] with Traverse[Try] with MonadRec[Try] = + new TryCoflatMap with MonadError[Try, Throwable] with Traverse[Try] with MonadRec[Try] { def pure[A](x: A): Try[A] = Success(x) override def pureEval[A](x: Eval[A]): Try[A] = x match { @@ -57,6 +58,13 @@ trait TryInstances extends TryInstances1 { case f: Failure[_] => G.pure(castFailure[B](f)) } + @tailrec final def tailRecM[B, C](b: B)(f: B => Try[(B Xor C)]): Try[C] = + f(b) match { + case f: Failure[_] => castFailure[C](f) + case Success(Xor.Left(b1)) => tailRecM(b1)(f) + case Success(Xor.Right(c)) => Success(c) + } + def handleErrorWith[A](ta: Try[A])(f: Throwable => Try[A]): Try[A] = ta.recoverWith { case t => f(t) } diff --git a/tests/src/test/scala/cats/tests/TryTests.scala b/tests/src/test/scala/cats/tests/TryTests.scala index 1d79a4ea35..9ded67b731 100644 --- a/tests/src/test/scala/cats/tests/TryTests.scala +++ b/tests/src/test/scala/cats/tests/TryTests.scala @@ -21,6 +21,9 @@ class TryTests extends CatsSuite { checkAll("Try[Int] with Option", TraverseTests[Try].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[Try]", SerializableTests.serializable(Traverse[Try])) + checkAll("Try", MonadRecTests[Try].monadRec[Int, Int, Int]) + checkAll("MonadRec[Try]", SerializableTests.serializable(MonadRec[Try])) + test("show") { forAll { fs: Try[String] => fs.show should === (fs.toString)