Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why `scala.util.Try` is not mentioned in chapter "Handling errors without exceptions" of book "functional programming in Scala"?

Tags:

In the chapter "Handling errors without exceptions" of book "functional programming in Scala", the author gives:

  1. The problem of throwing exceptions from the body of a function
  2. Use Option if we don't care about the actual exception
  3. Use Either if we care about the actual exception

But scala.util.Try is not mentioned. From my point of view, I think Try is very suitable when we care about the actual exception, why it's not mentioned? Is there any reason I have missed?

like image 988
Freewind Avatar asked Jul 14 '15 09:07

Freewind


People also ask

How do you handle exceptions in Scala?

try/catch/finally A basic way we can handle exceptions in Scala is the try/catch/finally construct, really similar to the Java one. In the following example, to make testing easier, we'll return a different negative error code for each exception caught: def tryCatch(a: Int, b: Int): Int = { try { return Calculator.

What is try in Scala?

The Try type represents a computation that may either result in an exception, or return a successfully computed value. It's similar to, but semantically different from the scala. util. Either type. Instances of Try[T] , are either an instance of scala.


1 Answers

I'm neither of the authors of Functional Programming in Scala, but I can make a few guesses about why they don't mention Try.

Some people don't like the standard library's Try because they claim it violates the functor composition law. I personally think that this position is kind of silly, for the reasons Josh Suereth mentions in the comments of SI-6284, but the debate does highlight an important aspect of Try's design.

Try's map and flatMap are explicitly designed to work with functions that may throw exceptions. People from the FPiS school of thought (including me) would tend to suggest wrapping such functions (if you absolutely have to deal with them at all) in safe versions at a low level in your program, and then exposing an API that will never throw (non-fatal) exceptions.

Including Try in your API muddles up the layers in this model—you're guaranteeing that your API methods won't throw exceptions, but then you're handing people a type that's designed to be used with functions that throw exceptions.

That's only a complaint about the standard library's design and implementation of Try, though. It's easy enough to imagine a version of Try with different semantics, where the map and flatMap methods didn't catch exceptions, and there would still be good reasons to avoid this "improved" version of Try whenever possible.

One of these reasons is that using Either[MyExceptionType, A] instead of Try[A] makes it possible to get more mileage out of the compiler's exhaustivity checking. Suppose I'm using the following simple ADT for errors in my application:

sealed class FooAppError(message: String) extends Exception(message)  case class InvalidInput(message: String) extends FooAppError(message) case class MissingField(fieldName: String) extends FooAppError(   s"$fieldName field is missing" ) 

Now I'm trying to decide whether a method that can only fail in one of these two ways should return Either[FooAppError, A] or Try[A]. Choosing Try[A] means we're throwing away information that's potentially useful both to human users and to the compiler. Suppose I write a method like this:

def doSomething(result: Either[FooAppError, String]) = result match {   case Right(x) => x   case Left(MissingField(_)) => "bad" } 

I'll get a nice compile-time warning telling me that the match is not exhaustive. If I add a case for the missing error, the warning goes away.

If I had used Try[String] instead, I'd also get exhaustivity checking, but the only way to get rid of the warning would be to have a catch-all case—it's just not possible to enumerate all Throwables in the pattern match.

Sometimes we actually can't conveniently limit the kinds of ways an operation can fail to our own failure type (like FooAppError above), and in these cases we can always use Either[Throwable, A]. Scalaz's Task, for example, is essentially a wrapper for Future[Throwable \/ A]. The difference is that Either (or \/) supports this kind of signature, while Try requires it. And it's not always what you want, for reasons like useful exhaustivity checking.

like image 175
Travis Brown Avatar answered Oct 21 '22 11:10

Travis Brown