Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Traverse[Try] #1083

Merged
merged 2 commits into from
Jun 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions core/src/main/scala/cats/std/try.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package std

import cats.syntax.all._
import cats.data.Xor
import TryInstances.castFailure

import scala.util.control.NonFatal
import scala.util.{Failure, Success, Try}

trait TryInstances extends TryInstances1 {

implicit def catsStdInstancesForTry: MonadError[Try, Throwable] with CoflatMap[Try] =
new TryCoflatMap with MonadError[Try, Throwable]{
implicit def catsStdInstancesForTry: MonadError[Try, Throwable] with CoflatMap[Try] with Traverse[Try] =
new TryCoflatMap with MonadError[Try, Throwable] with Traverse[Try] {
def pure[A](x: A): Try[A] = Success(x)

override def pureEval[A](x: Eval[A]): Try[A] = x match {
Expand All @@ -20,24 +21,42 @@ trait TryInstances extends TryInstances1 {

override def product[A, B](ta: Try[A], tb: Try[B]): Try[(A, B)] = (ta, tb) match {
case (Success(a), Success(b)) => Success((a, b))
case (f: Failure[_], _) => f.asInstanceOf[Try[(A, B)]]
case (_, f: Failure[_]) => f.asInstanceOf[Try[(A, B)]]
case (f: Failure[_], _) => castFailure[(A, B)](f)
case (_, f: Failure[_]) => castFailure[(A, B)](f)
}

override def map2[A, B, Z](ta: Try[A], tb: Try[B])(f: (A, B) => Z): Try[Z] = (ta, tb) match {
case (Success(a), Success(b)) => Try(f(a, b))
case (f: Failure[_], _) => f.asInstanceOf[Try[Z]]
case (_, f: Failure[_]) => f.asInstanceOf[Try[Z]]
case (f: Failure[_], _) => castFailure[Z](f)
case (_, f: Failure[_]) => castFailure[Z](f)
}

override def map2Eval[A, B, Z](ta: Try[A], tb: Eval[Try[B]])(f: (A, B) => Z): Eval[Try[Z]] =
ta match {
case f: Failure[_] => Now(f.asInstanceOf[Try[Z]])
case f: Failure[_] => Now(castFailure[Z](f))
case Success(a) => tb.map(_.map(f(a, _)))
}

def flatMap[A, B](ta: Try[A])(f: A => Try[B]): Try[B] = ta.flatMap(f)

def foldLeft[A, B](fa: Try[A], b: B)(f: (B, A) => B): B =
fa match {
case Success(a) => f(b, a)
case Failure(_) => b
}

def foldRight[A, B](fa: Try[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
fa match {
case Success(a) => f(a, lb)
case Failure(_) => lb
}

def traverse[G[_], A, B](fa: Try[A])(f: A => G[B])(implicit G: Applicative[G]): G[Try[B]] =
fa match {
case Success(a) => G.map(f(a))(Success(_))
case f: Failure[_] => G.pure(castFailure[B](f))
}

def handleErrorWith[A](ta: Try[A])(f: Throwable => Try[A]): Try[A] =
ta.recoverWith { case t => f(t) }

Expand Down Expand Up @@ -78,6 +97,14 @@ trait TryInstances extends TryInstances1 {
}
}

private[std] object TryInstances {
/**
* A `Failure` can be statically typed as `Try[A]` for all `A`, because it
* does not actually contain an `A` value (as `Success[A]` does).
*/
@inline final def castFailure[A](f: Failure[_]): Try[A] = f.asInstanceOf[Try[A]]
}

private[std] sealed trait TryInstances1 extends TryInstances2 {
implicit def catsStdMonoidForTry[A: Monoid]: Monoid[Try[A]] =
new TryMonoid[A]
Expand Down
3 changes: 3 additions & 0 deletions tests/src/test/scala/cats/tests/TryTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class TryTests extends CatsSuite {
checkAll("Try with Throwable", MonadErrorTests[Try, Throwable].monadError[Int, Int, Int])
checkAll("MonadError[Try, Throwable]", SerializableTests.serializable(MonadError[Try, Throwable]))

checkAll("Try[Int] with Option", TraverseTests[Try].traverse[Int, Int, Int, Int, Option, Option])
checkAll("Traverse[Try]", SerializableTests.serializable(Traverse[Try]))

test("show") {
forAll { fs: Try[String] =>
fs.show should === (fs.toString)
Expand Down