Mike Slinn

Either, Left and Right

— Draft —

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/src/main/scala/EitherDemo.scala.

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 REPL
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 REPL showing type error
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 REPL showing type error
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 REPL
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 REPL
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 REPL
scala> result1.isRight
scala> res0: Boolean = true
scala>
result1.isLeft res1: Boolean = false

Storing a value into Either.left is similarly simple.

Scala REPL
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 REPL
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 REPL
scala> val rightAgain = result1.value
scala> rightAgain: String = yay! 

We can also use an extractor to retrieve the value from a Left:

Scala REPL
scala> val Left(left) = result2
scala> left: String = String: Oops, I did it again! 

This also works:

Scala REPL
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.

Scala code
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 REPL
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 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. The Scala 2.12 Changes section discusses the extra capabilities added to 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 REPL
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.

sealed abstract class Either [+A, +B] extends AnyRef

To refresh your memory, result2 was defined like this:

Scala code
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:

Scala code
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 REPL showing type error
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 REPL showing type error
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 REPL
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].

Scala code
type StringOrInt = Either[String, Int]

Let's use the type alias to shorten the above code.

Scala 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.

Shell
$ 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:

Scala code
object WordSearch {
  def main(args: Array[String]): Unit = {
  }
}

Let's define a type alias for the return type, inside the body of WordSearch:

Scala code
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.

Scala code
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:

Scala code
List("word", "oink", "blahbla", "another").foreach { w =>
  println(s"$w: ${search(list, w)}")
}

So we now have:

Scala code
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/src/main/scala/solutions/WordSearch.scala. Examine each one – you will learn something with each solution.

Scala code
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:

Shell
$ 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:

  • map and flatMap, which means for-comprehensions now can be used.
  • contains, toOption, toTry, toSeq and filterOrElse.

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.

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.