23

Is it possible to handle Either in similar way to Option? In Option, I have a getOrElse function, in Either I want to return Left or process Right. I'm looking for the fastest way of doing this without any boilerplate like:

val myEither:Either[String, Object] = Right(new Object())
myEither match {
    case Left(leftValue) => value
    case Right(righValue) => 
        "Success"
}
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
Michał Jurczuk
  • 3,728
  • 4
  • 32
  • 56

5 Answers5

27

In Scala 2.12,

Either is right-biased, which means that Right is assumed to be the default case to operate on. If it is Left, operations like map, flatMap, ... return the Left value unchanged

so you can do

myEither.map(_ => "Success").merge

if you find it more readable than fold.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
7

You can use .fold:

scala> val r: Either[Int, String] = Right("hello")
r: Either[Int,String] = Right(hello)

scala> r.fold(_ => "got a left", _ => "Success")
res7: String = Success

scala> val l: Either[Int, String] = Left(1)
l: Either[Int,String] = Left(1)

scala> l.fold(_ => "got a left", _ => "Success")
res8: String = got a left

Edit:

Re-reading your question it's unclear to me whether you want to return the value in the Left or another one (defined elsewhere)
If it is the former, you can pass identity to .fold, however this might change the return type to Any:

scala> r.fold(identity, _ => "Success")
res9: Any = Success
Marth
  • 23,920
  • 3
  • 60
  • 72
7

Both cchantep's and Marth's are good solutions to your immediate problem. But more broadly, it's difficult to treat Either as something fully analogous to Option, particularly in letting you express sequences of potentially failable computations for comprehensions. Either has a projection API (used in cchantep's solution), but it is a bit broken. (Either's projections break in for comprehensions with guards, pattern matching, or variable assignment.)

FWIW, I've written a library to solve this problem. It augments Either with this API. You define a "bias" for your Eithers. "Right bias" means that ordinary flow (map, get, etc) is represented by a Right object while Left objects represent some kind of problem. (Right bias is conventional, although you can also define a left bias if you prefer.) Then you can treat the Either like an Option; it offers a fully analogous API.

import com.mchange.leftright.BiasedEither

import BiasedEither.RightBias._

val myEither:Either[String, Object] = ...
val o = myEither.getOrElse( "Substitute" )

More usefully, you can now treat Either like a true scala monad, i.e. use flatMap, map, filter, and for comprehensions:

val myEither : Either[String, Point] = ???
val nextEither = myEither.map( _.x ) // Either[String,Int]

or

val myEither : Either[String, Point] = ???
def findGalaxyAtPoint( p : Point ) : Either[String,Galaxy] = ???

val locPopPair : Either[String, (Point, Long)] = {
  for { 
    p <- myEither    
    g <- findGalaxyAtPoint( p )
  } yield {
    (p, g.population)
  }
}

If all processing steps succeeded, locPopPair will be a Right[Long]. If anything went wrong, it will be the first Left[String] encountered.

It's slightly more complex, but a good idea to define an empty token. Let's look at a slight variation on the for comprehension above:

val locPopPair : Either[String, (Point, Long)] = {
  for { 
    p <- myEither    
    g <- findGalaxyAtPoint( p ) if p.x > 1000
  } yield {
    (p, g.population)
  }
}

What would happen if the test p.x > 1000 failed? We'd want to return some Left that signifies "empty", but there is no universal appropriate value (not all Left's are Left[String]. As of now, what would happen is the code would throw a NoSuchElementException. But we can specify an empty token ourselves, as below:

import com.mchange.leftright.BiasedEither

val RightBias = BiasedEither.RightBias.withEmptyToken[String]("EMPTY")
import RightBias._

val myEither : Either[String, Point] = ???
def findGalaxyAtPoint( p : Point ) : Either[String,Galaxy] = ???

val locPopPair : Either[String, (Point, Long)] = {
  for { 
    p <- myEither    
    g <- findGalaxyAtPoint( p ) if p.x > 1000
  } yield {
    (p, g.population)
  }
}

Now, if the p.x > 1000 test fails, there will be no Exception, locPopPair will just be Left("EMPTY").

wwkudu
  • 2,778
  • 3
  • 28
  • 41
Steve Waldman
  • 13,689
  • 1
  • 35
  • 45
2

I guess you can do as follows.

def foo(myEither: Either[String, Object]) = 
  myEither.right.map(rightValue => "Success")
cchantep
  • 9,118
  • 3
  • 30
  • 41
  • Looks like `foo` should return a `String`, not a `Either`. Aren't you missing a `.getOrElse(value)` at the end? – Marth Dec 31 '15 at 13:28
  • "either I want to return Left or process Right" is what's done by mapping the right projection. For an `Either[A, B]` the goal is to get either a `A` (provided a `B => A` for `Right`) or a `B` (provided a `A => B` for `Left`), then `.fold` is your friend. – cchantep Dec 31 '15 at 15:16
  • You're right, I read the question too quickly. I just looked at the code in it and thought the question was "how do I write this more succinctly". My bad. – Marth Dec 31 '15 at 15:35
-1

In scala 2.13, you can use myEither.getOrElse

   Right(12).getOrElse(17) // 12
   Left(12).getOrElse(17)  // 17
  • This does the opposite of what was requested. The OP wants to return the **Left** and replace the **Right**. – jwvh Nov 09 '20 at 07:38