Either as an Effect

Naturally, methods defined in terms of an effect are also right-biased for Either. So is, for example, the callback method foreach, which we already know from Option:

scala> val left = Left("HoHoHo").withRight[BigDecimal]
left: scala.util.Either[String,BigDecimal] = Left(HoHoHo)
scala> left.foreach(println)
scala> left.swap.foreach(println)
HoHoHo

In the preceding example, the callback is not executed for the left, but is called as soon as it becomes Right after we call swap on it. The filtering has a bit of a different definition, as it accepts a predicate to filter the right side, and a value to return as a Left if the predicate does not hold:

scala> left.swap.filterOrElse(_.length > 10, "Sorry, too short")
res27: ... = Left(Sorry, too short)

map and flatMap allow you to transform the right side if you provide the appropriate functions. flatMap expects the result of the function to have a type of Either as well. To demonstrate this, we'll reuse our Option example:

val buyBait: String => Bait = ???
val makeBait: String => Bait = ???
val castLine: Bait => Line = ???
val hookFish: Line => Fish = ???

But this time we'll start with bestBaitForFish, which is the result of us asking about this other fisherman. The fisherman may be in a bad mood and we might hear them cursing instead of the hint we're expecting to get. These are both of the String type, but we absolutely want to differentiate between them:

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

Again, we're not living up to the standards we've defined for ourselves. We might get an explanation from the seller in the shop as to why we can't have the bait we want to buy. In the case that we fail to make bait, cast a line, or hook a fish, we might express ourselves verbally as well with some text that we will not put in the examples of this book. It makes sense to express the possibility that our functions return this verbal feedback if something goes wrong:

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

Now, we can rewrite the code that used map with flatMap. It makes sense to write it as a for comprehension:

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

The calls will be carried over until the last one succeeds or one of them produces a Left. In the second case, the first Left we meet will be returned as the result of the function call.

In the preceding example, we used the fold method, which allows us to apply the given functions to one side of Either. In our use case, we did this to ignore any eventual error message that will be returned by the seller in the shop and to make the bait ourselves. If we succeed, we wrap the bait into the Right before returning it so that we have proper type alignment.

The fold method is unbiased as it treats the left and right sides of Either equally.

In the last example we looked at, the model we represented with Either had its left side dedicated to the description of failures that happened during its operation. It is always useful to have the type of the error  more specific than String. Often, especially in cases involving integration with Java, the most suitable choice would be to represent errors as subtypes of the Exception. In fact, this is so ubiquitous that there is a special effect available for this in Scala called Try. An Either having a type of its left side inheriting from a Throwable can be converted into a Try with the respective method:

def toTry(implicit ev: A <:< Throwable): Try[B] = this match {
case Right(b) => Success(b)
case Left(a) => Failure(a)
}

Let's examine cases in which Try is a better choice then Either and learn how to use it.

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

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