Published 2014-01-12.
Last modified 2016-10-14.
Time to read: 6 minutes.
Either is a Scala type that can hold a value which has one of two possible types. This lecture compares and contrasts Either, Try and Option.
The sample code for this lecture can be found in
courseNotes/.
Scala's Either type holds information about entities which can assume one of two states.
An Either instance can only hold one value,
which is either wrapped by a Left instance or a Right instance.
If an Either instance is of type Right,
we say that the instance is a Right;
if an Either instance has type Left, we say that the instance is a Left.
Either is immutable, so once an instance has been assigned a value it cannot be changed.
Either is parametric in two types, which are often different from each other.
We will discuss parametric types (also known as generics) in detail in the
Parametric Types lecture of the
Intermediate Scala course.
For example, you could decide that information about live things will be a Right,
and information about non-living things will be Left.
Either
wraps both possibilities.
Right and Left are subclasses of Either,
and are stored into the right and left properties of Either instances,
respectively, as shall see in a moment.
We can refer to the value of the right property as the right projection,
and the value of the left property as the left projection.
Declare the Type
You could declare an immutable value of type Either[NonLivingThing, LivingThing]
and assign it a value.
scala> case class LivingThing(name: String, species: String) defined class LivingThing
scala> case class NonLivingThing(name: String, description: String) defined class NonLivingThing
scala> val thing1: Either[NonLivingThing, LivingThing] = Right(LivingThing("Leatherback Turtle", "Dermochelys coriacea")) thing1: Either[NonLivingThing,LivingThing] = Right(LivingThing(Leatherback Turtle,Dermochelys coriacea))
Notice that I wrapped the LivingThing instance within a Right instance.
The Scala compiler is not smart enough to figure out that a LivingThing belongs on the Right.
scala> val thing1: Either[NonLivingThing, LivingThing] = LivingThing("Leatherback Turtle", "Dermochelys coriacea") scala> <console>:15: error: type mismatch; found : LivingThing required: Either[NonLivingThing,LivingThing] val thing1: Either[NonLivingThing, LivingThing] = LivingThing("Leatherback Turtle", "Dermochelys coriacea")
Also notice that I declared the full type of thing1.
If I did not do that, the Left would have type Nothing.
scala> val thing3 = Right(LivingThing("Leatherback Turtle", "Dermochelys coriacea")) scala> thing1: scala.util.Right[Nothing,LivingThing] = Right(LivingThing(Leatherback Turtle,Dermochelys coriacea))
Similarly, here is how to create an instance using Left:
scala> val thing2: Either[NonLivingThing, LivingThing] = Left(NonLivingThing("Opal", "Hydrated silica")) scala> thing2: Either[NonLivingThing,LivingThing] = Left(NonLivingThing(Opal,Hydrated silica))
Similar Left and Right Types
This example shows the Left and Right both having the same type, String.
Here is an Either[String, String].
scala> val result1: Either[String, String] = Right("yay!") scala> result1: Either[String,String] = Right(yay!)
You can test to see if an Either instance holds its value on the left or on the right.
scala> result1.isRight scala> res0: Boolean = true
scala> result1.isLeft res1: Boolean = false
Storing a value into Either.left is similarly simple.
scala> val result2: Either[String, String] = Left("Oops, I did it again!") scala> result2: Either[String,String] = Left(java.lang.String)
scala> result2.isRight scala> res3: Boolean = false
scala> result2.isLeft scala> res4: Boolean = true
Extract the Value
Here is one way to extract the value from the right projection of an Either instance.
We discussed extractors in the
Unapply and
Sealed Classes and Extractors
lectures earlier in this course.
scala> val Right(right) = result1 scala> right: String = yay!
Scala 2.12 introduced the value property for Right and Left,
so we can also write:
scala> val rightAgain = result1.value scala> rightAgain: String = yay!
We can also use an extractor to retrieve the value from a Left:
scala> val Left(left) = result2 scala> left: String = String: Oops, I did it again!
This also works:
scala> val leftAgain = result2.value scala> leftAgain: String = String: Oops, I did it again!
A common use of Either is as an alternative to Option for dealing with
possible failures, because it can return information about a failure.
For example, you could use Either[String, Int] to decode a String
into an Int on the Right,
or return the unparseable string on the Left.
def parse(in: String): Either[String, Int] = try {
Right( t)
} catch {
case e: Exception =>
Left(in)
}
def show(either: Either[String, Int]): Unit =
println(either match {
case Right(x) =>
s"Parsed Int: $x"
case Left(x) =>
s"Unparseable String: $x"
})
Now let's try parsing some strings:
scala> show(parse("1234")) scala> Parsed Int: 1234
scala> show(parse("12abc")) Unparseable String: 12abc
scala> show(parse("abc123")) Unparseable String: abc123
Many methods might return a different type of value when an error occurs.
Since Scala 2.10 we have the
scala.util.Try type,
described in the Try and try/catch/finally lecture.
Try is similar to Either, but with an Exception on the left side.
If you find yourself wanting to return an Either,
with an Exception on the left side for the failure case,
you should return a Try instead.
However, many uses for Either do not require the value on the right to mean success
and the value on the left to mean failure.
Unlike
The Scala 2.12 Changes section discusses the extra capabilities added to Try and Option, Either is not right-biased;
this means that Either has been designed to give equal weight for the Left and
Right subclasses.
You'll realize how this affects your code as you work with Either, Try and
Option.Either with Scala 2.12,
and the Option vs. Either vs. Try
section at the end of this lecture contrasts these three types.
Processing Either Efficiently with fold
As we have just seen, Either can hold one of two values.
What if you wanted to write code that checks to see if the result was a Right or a Left,
and return the same type of result, say a String, for display?
It would be desirable to be able to write code that handles both cases without writing a lot of boilerplate.
This use case is handled by the
fold combinator,
which is a higher-order function that accepts two Function1s,
one for each of the Left and Right cases.
Higher-order functions will be discussed in detail in the Higher-Order Functions lecture of the Intermediate Scala course.
Here is an example of what fold looks like in action:
scala> result2.fold( | lhs => "Handled left side", | rhs => "Handled right side" | ) scala> res0: String = Handled left side
It would be difficult to understand your first encounter with fold if you never looked at the
Either class definition and the fold method signature at least once.
This is partially because Either.fold specifies the type of each of the Function1s
so we do not need to declare the types of the lambdas shown above.
The Either class declaration shows that it is parametric in A and B;
the type of the Left side of Either is A,
and the type of the Right side of Either is B.
To refresh your memory, result2 was defined like this:
val result2: Either[String, String] = Left("Oops, I did it again!")
This declaration told the Scala compiler that parametric types A and B are both actually
String for this instance of Either.
The fold method signature is:
fold[C](fa: A => C, fb: B => C): C
This means that fold is parametric in C,
and accepts two Function1s that both return the same parametric type, namely C.
From the fold method signature,
the Scala compiler knows that fa (on the Left) has type A => C and
fb (on the Right) has type B => C.
In other words, the first Function1, fa,
accepts an A (which is the type of the Left side of this Either)
and returns a C,
while the second Function1, fb, accepts a B
(which is the type of the Right side of this Either)
and also returns a C.
If both fa and fb do not return the same type the compiler will issue an error.
Again, fold requires that both Function1s passed to it return the same type,
and that type is referred to as C in the fold method signature.
The Scala compiler deduces that C must be String for this fold invocation.
This means that the Scala compiler knows that the types of fa and fb
are both actually String => String for this invocation of fold.
You do not need to declare this explicitly,
but if you want to express the types written explicitly you could write this instead:
scala> result2.fold[String]( | (lhs: String) => "Handled left side", | (rhs: String) => "Handled right side" | ) scala> res5: String = Handled left side
In the above, the first type, in square braces, declares C to be String.
The type of lhs reaffirms A to be String;
remember that when the Either instance was created,
A was defined to be String,
so if you put any other type here the compiler will give an error.
Let's try it and see:
scala> result2.fold[String]( | (lhs: Int) => "Handled left side", | (rhs: String) => "Handled right side" | ) scala> <console>:10: error: type mismatch; found : Int => String required: String => String (lhs: Int) => "Handled left side",
Similarly, the type of rhs reaffirms B to be String;
again, B was declared as String when the Either was created.
If we explicitly declare C to be a specific type,
that type must match the inferred type deduced by the compiler.
Let's see what happens when we declare C to be something other than String:
scala> result2.fold[Int]( | (lhs: String) => "Handled left side", | (rhs: String) => "Handled right side" | ) scala> <console>:10: error: type mismatch; found : String("Handled left side") required: Int (lhs: String) => "Handled left side", ^ <console>:11: error: type mismatch; found : String("Handled right side") required: Int (rhs: String) => "Handled right side"
Type Alias
We can define a type alias using the type keyword.
It is usually better to define a type alias with a descriptive name than to write out a long type
like Either[String, Int].
type StringOrInt = Either[String, Int]
Let's use the type alias to shorten the above code.
def parse(in: String): StringOrInt = try { Right(in.toInt) } catch { case e: Exception => Left(in) }
def show(either: StringOrInt): Unit = println(either match { case Right(x) => s"Parsed Int: $x"
case Left(x) => s"Unparseable String: $x" })
Exercise
The Either type in the Scala library can be used for algorithms that return
either a result or some failure information.
Write a method that takes two parameters: a list of words and a word to match.
Return an Either containing the index of the matching word in the list on the
right or the word that did not match on the left.
To create a console application, we will clone the
sbtTemplate
project on GitHub that we learned about in
SBT Project Setup lecture.
We will clone into a directory called eitherExercise.
$ git clone https://github.com/mslinn/sbtTemplate.git eitherExercise
$ cd eitherExercise
Now lets edit a new file in eitherExercise called src/main/scala/WordSearch.scala
using an IDE or your favorite text editor.
To define an entry point in that file, simply write:
object WordSearch {
def main(args: Array[String]): Unit = {
}
}
Let's define a type alias for the return type, inside the body of WordSearch:
object WordSearch {
def main(args: Array[String]): Unit = {
type StringOrInt = Either[String, Int]
}
}
Let's also decide on what the signature will be for the method that will do the work.
Note that ??? defines a method body that does nothing –
it is a placeholder for the body which we will write later.
This allows the code to compile before it is completely written.
object WordSearch {
def main(args: Array[String]): Unit = {
type StringOrInt = Either[String, Int]
def search(list: List[String], word: String): StringOrInt = ???
}
}
We will use this code to run the search method:
List("word", "oink", "blahbla", "another").foreach { w =>
println(s"$w: ${search(list, w)}")
}
So we now have:
object WordSearch {
def main(args: Array[String]): Unit = {
type StringOrInt = Either[String, Int]
def search(list: List[String], word: String): StringOrInt = ???
List("word", "oink", "blahbla", "another").foreach { w =>
println(s"$w: ${search(list, w)}")
}
}
}
Notice that the entry point application consists of a type alias, a method definition and some code that is executed.
Solution
Some solutions are provided in
courseNotes/.
Examine each one – you will learn something with each solution.
package solutions
/** Two solutions are shown. Solution 2 is best. */ object WordSearch extends App { val list = List("word", "another", "more")
type StringOrInt = Either[String, Int]
/** Efficient but clumsy */ def search(list: List[String], word: String): StringOrInt = { val index = list.indexOf(word) if (index == -1) Left(word) else Right(index) }
/** Efficient, simple, elegant. * Here we see Scala's match keyword used as a multi-way branch. * Each match is tried in order until one succeeds. * In the second case, notice that the variable index is defined. It will match every value. */ def search2(list: List[String], word: String): StringOrInt = list.indexOf(word) match { case -1 => Left(word) case index => Right(index) }
List("word", "oink", "blahbla", "another").foreach { w => println(s"Method 1 search for '$w': ${search(list, w)}") println(s"Method 2 search for '$w': ${search2(list, w)}") } }
You can run them like this:
$ sbt "runMain solutions.WordSearch" Method 1 word: Right(0) Method 2 word: Right(0) Method 1 oink: Left(oink) Method 2 oink: Left(oink) Method 1 blahbla: Left(blahbla) Method 2 blahbla: Left(blahbla) Method 1 another: Right(1) Method 2 another: Right(1)
Scala 2.12 Changes
Either is a right-biased monad, but the changes are backwards-compatible,
so existing code that uses Either should continue to work.
The following methods have been added:
mapandflatMap, which means for-comprehensions now can be used.-
contains,toOption,toTry,toSeqandfilterOrElse.
Here are the notes for the pull request to the Scala compiler.
Prior to Scala 2.12, Either did not work directly with for-comprehensions.
We will discuss this topic in detail in the Either is Right-Biased With Scala 2.12
section of the For-Loops and For-Comprehensions Examples lecture of the
Intermediate Scala course.
Option vs. Either vs. Try
Here is a table that provides guidelines for when you might use each of these constructs:
| Option | Either | Try | |
|---|---|---|---|
| No problem encountered | Returns Some(value)
| Returns Right(value)
| Returns Success(value)/td>
|
| Problem encountered | Returns None and ignores problem
| Returns Left(value) that contains information about the problem, or original data that could not be processed.
| Returns Failure(exception) that contains Exception for later error handling
|
| Use case | Only the happy path matters; no error handling required. Simple to work with. |
Use case #1: Data about failure is as important as the successful result.
Largely supplanted by Try since Scala 2.10.
Use case #2: There is no requirement for Either's left hand side to have any relationship with
failure – instead, it is possible that the program requires a variable to contain a value with one of two types.
| Successful result is important, also errors must be handled. This is the best choice for most use cases involving error handling. |
© Copyright 1994-2024 Michael Slinn. All rights reserved.
If you would like to request to use this copyright-protected work in any manner,
please send an email.
This website was made using Jekyll and Mike Slinn’s Jekyll Plugins.