Try as an effect

Try offers the same functionality as an Option in terms of filtering its results with a predicate. If the predicate does not hold, the result is represented as a Failure[NoSuchElementException]. Taking the line definition from our previous example, like so:

scala> line.filter(_.nonEmpty)
res38: scala.util.Try[String] = Success(Hi, I'm the success!)
scala> line.filter(_.isEmpty)
res39: scala.util.Try[String] = Failure(java.util.NoSuchElementException: Predicate does not hold for Hi, I'm the success!)

collect works in the same way, but it takes a partial function and allows us to filter and transform the contents of the Try at the same time:

scala> line.collect { case s: String => s * 2 }
res40: scala.util.Try[String] = Success(Hi, I'm the success!Hi, I'm the success!)
scala> line.collect { case s: "Other input" => s * 10 }
res41: scala.util.Try[String] = Failure(java.util.NoSuchElementException: Predicate does not hold for Hi, I'm the success!)

The filter and collect functions are Success biased, and so are map and flatMap. Let's reimplement the fishing example in this situation, where our parameter is of type Try[String], and the exceptions are replacing strings as the problem descriptions we had in our example of Either:

def goFishing(bestBaitForFishOrCurse: Try[String]): Try[Fish] =
bestBaitForFishOrCurse.map(buyBait).map(castLine).map(hookFish)

The operations are chained on the Success. Yet again, we have to fix the signatures of our functions so that they encode the possibility of an error in every step in the type of the result:

val buyBait: String => Try[Bait]
val makeBait: String => Try[Bait]
val castLine: Bait => Try[Line]
val hookFish: Line => Try[Fish]

Now, we have to use flatMap instead of map to align the types. Again, it is more readable if represented as a for comprehension:

def goFishing(bestBaitForFishOrCurse: Try[String]): Try[Fish] = for {
baitName <- bestBaitForFishOrCurse
bait <- buyBait(baitName).fold(_ => makeBait(baitName), Success(_))
line <- castLine(bait)
fish <- hookFish(line)
} yield fish

This implementation is almost identical to the one we have for Either, with the exception that we now have to wrap successful calls into Success and not into Right (we have to use a different constructor for an effect).

The fold is one of the methods that is unbiased for Try. It takes arguments to transfer both Success and Failure, as shown in the preceding code. Another unbiased method is transform, which is similar to fold, but takes functions for returning Try as parameters. In a sense, transform could be called flatFold:

scala> line.transform((l: String) => Try(println(l)), (ex: Throwable) => Try(throw ex))
Hi, I'm the success!
res45: scala.util.Try[Unit] = Success(())

There are also a few functions that are Failure biased. 

recover and recoverWith apply the given partial function to the Failure. They are basically duals of map and flatMaps, but for the exception side:

line.recover {
case ex: NumberFormatException => Math.PI
}
line.recoverWith {
case ex: NoSuchElementException => Try(retryAfterDelay)
}

The orElse method allows us to chain Failures in the same way we did with None:

val result = firstTry orElse secondTry orElse failure orElse success

As we can see, Try is similar to Option and Either, and it should not come as a surprise that it can be converted to both Option and Either[Throwable, _]:

scala> line.toOption
res51: Option[String] = Some("Hi, I'm the success!")
scala> line.toEither
res53: scala.util.Either[Throwable,String] = Right("Hi, I'm the success!")

There is one more effect in the standard library that is a bit different from the three we just looked at because it takes into account a more subtle aspect of calling a function – the time it is going to take to return the result.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.198.174