Use of Either in Scala
In this article, we are going to see the use of Either in Scala. We use options in Scala but why do we want to go for Either? Either is a better approach.
Join the DZone community and get the full member experience.
Join For FreeIn this article, we are going to see the use of Either in scala. We use Options in scala but why do we want to go for Either?
Either is a better approach in the respect that if something fails we can track down the reason, which in Option None case is not possible.
We simply pass None but what is the reason we got None instead of Some. We will see how to tackle this scenario using Either.
xxxxxxxxxx
Either[Left, Right]
None is similar to Left which signifies Failure and Some is similar to Right which signifies Success.
Let's see with help of an example:
def returnEither(value: String): Either[NumberFormatException, Int] =
{
try {
Right(value.toInt)
} catch {
case ex: NumberFormatException => Left(ex)
}
}
returnEither("abc").map(x => println(x))
It will not print anything, Either is right biased and returnEither("abc") gives Left(java.lang.NumberFormatException: For input string: "abc")
Now let's call it on Right value.
xxxxxxxxxx
returnEither("1").map(x => println(x))
It will print 1. Yes, it is right biased as the map works on Either.right. What if i want to call map on left?
xxxxxxxxxx
returnEither("abc").left.map(x => println(x))
It will print java.lang.NumberFormatException: For input string: "abc".
Using Match case with Either
xxxxxxxxxx
returnEither("1") match
{
case Right(value) => println(s"Right value: $value")
case Left(ex: NumberFormatException) => println(ex)
}
It will print Right value: 1
Extract value from Either
Let's say we want to extract left value from Either.
xxxxxxxxxx
println(returnEither("abc").left)
will print LeftProjection(Left(java.lang.NumberFormatException: For input string: "abc"))
xxxxxxxxxx
println(returnEither("1").left)
will print LeftProjection(Right(1))
xxxxxxxxxx
println(returnEither("abc").left.get)
will print java.lang.NumberFormatException: For input string: "abc".
xxxxxxxxxx
println(returnEither("1").left.get)
will print: Exception in thread "main" java.util.NoSuchElementException: Either.left.get on Right
Oops. It had the right value.
We can use getOrElse or fold for default value.
getOrElse
xxxxxxxxxx
println(returnEither("1").left.getOrElse(2)) // will print 2
fold
xxxxxxxxxx
println(returnEither("1").fold(
i => s"Found an exception: '$i'",
b => s"Found int: '$b'"
))
will print Found int: '1'
xxxxxxxxxx
println(returnEither("abc").fold(
i => s"Found an exception: '$i'",
b => s"Found int: '$b'"
))
will print Found an exception: 'java.lang.NumberFormatException: For input string: "abc"'
Check value is Left or Right
xxxxxxxxxx
println(returnEither("abc").isLeft) //print true
println(returnEither("1").isRight) // print true
Working with Lists using Either
xxxxxxxxxx
val list: List[Either[Int, String]] = List(Right("r1"), Left(0), Right("r2"), Left(10), Right("r3"), Left(100), Right("r4"))
val newList = list.map(_.map(_.toUpperCase)).map(y => y.left.map(_ * 20))
println(newList)
Will give List(Right(R1), Left(0), Right(R2), Left(200), Right(R3), Left(2000), Right(R4)).
Let's understand how. As we know Either is right bias so list.map(x => x.map(_.toUpperCase)), x will only have Right Values which will be converted to uppercase then as we have return type as List[Either[Int, String]] for list.map(_.map(_.toUpperCase)). y will have left and right values. All the left values will be multiplied by 20.
toRight
y.toRight(x) checks if value is empty it return Left(x) else Right(y).
xxxxxxxxxx
val ok: Either[Error, String] = Some("Yeah!").toRight(new Error("Error!"))
val error: Either[Error, String] = None.toRight(new Error("Error!"))
println(ok) // Right(Yeah!)
println(error) // Left(java.lang.Error: Error!)
toLeft
y.toLeft(x) checks if value is empty it return Right(x) else Left(y).
xxxxxxxxxx
val okLeft: Either[Error, String] = Some(new Error("Error!")).toLeft("Yeah!")
val errorLeft: Either[Error, String] = None.toLeft("Yeah!")
println(okLeft) // Left(java.lang.Error: Error!)
println(errorLeft) // Right(Yeah!)
There could be a situation where one function is called then another and then another is a chain and the exceptions are thrown freely. How do we track which function actually caused the exception?
x
val array: Array[Int] = Array(0,2)
def fetchElement(el: Int): Either[Exception, Int] =
if (el < array.length ) Right(array(el))
else Left(new ArrayIndexOutOfBoundsException("from function fetchElement"))
def reciprocal(i: Int): Either[Exception, Double] =
if (i == 0) Left(new IllegalArgumentException("from function reciprocal"))
else Right(1.0 / i)
def stringify(d: Double): String = d.toString
def callFunctions(s: String): Either[Exception, String] = fetchElement(s.toInt).flatMap(reciprocal).map(stringify)
callFunctions("3") match {
case Left(_: ArrayIndexOutOfBoundsException) => println("Exception in fetchElement")
case Left(_: IllegalArgumentException) => println("Exception in reciprocal")
case Left(_) => println("got unknown exception")
case Right(s) => println(s"Got reciprocal: $s")
}
will print Exception in fetchElement.
If call callFunctions("0") will print Exception in reciprocal
Thanks for reading!
Published at DZone with permission of Jyoti Sachdeva. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments