1

I make a call to external function and in return have several Either. Say I have

val a = Right("hey")
val b = Right(2)
val c = Left("oops") .....

for{
 x <- a.right
 y <- b.right
 z <- c.right
} yield(User(x,y,z))

But say, if z is a Left as above. Then I wish to give it a default String. i.e. by

for{
 x <- a.right
 y <- b.right
 z <- c.right.getOrElse(Right("default String")).right
} yield(User(x,y,z))

It is dirty. How can I reduce this: c.right.getOrElse(Right("default String")).right. Doing c.right.getOrElse("default") will not work as a map on String returns IndexedSeq.

Jatin
  • 31,116
  • 15
  • 98
  • 163

4 Answers4

4

A simplified syntax can be defined by right-biasing Either implicitly, as suggested in this thread

val user = {
  implicit def rightBiasEither[A, B](e: Either[A, B]): Either.RightProjection[A,B] =
    e.right

  for {
    x <- a
    y <- b
    z <- c getOrElse (Right("default string"))
  } yield User(x, y, z)
}

You can choose where to explicitly have right-biasing using a limiting scope, as in the example code above, or wrapping the conversion in a custom object to import at will, as is customary.

pagoda_5b
  • 7,333
  • 1
  • 27
  • 40
1

You could write a function to do this:

def rightOrElse[A, B](e1: Either[A, B], e2: => Either[A, B]): Either[A, B] =
  e1.right.flatMap(_ => e2)

You'd still need to call .right on the result, to make the types match in your for-comprehension.

This is much easier to do with Scalaz's \/ (disjunction) type (an improved version of Either). Not only is it right-biased, avoiding the need to work through a right projection, but it has a richer API including an orElse method.

val a = \/.right("hey")
val b = \/.right(2)
val c = \/.left("oops")

for {
  x <- a
  y <- b
  z <- c orElse \/.right("default String")
} yield User(x, y, z)
Ben James
  • 121,135
  • 26
  • 193
  • 155
  • Any other prettier way using the scala api? – Jatin Aug 20 '13 at 08:44
  • 1
    If you will be working with Either-type constructs a lot, and particularly if you are intermixing them with Options and other Monadic types, you may well find it worth your while to bite the bullet and dive into Scalaz. Its disjunction type is a proper right-biased Monad and can interact better with other Monads via Monad Transformers such as EitherT. – Shadowlands Aug 20 '13 at 08:49
0

An alternative is to use scala.util.Try. In short, Try is like an Either that would both be right biased and force the left type to Throwable. If your left type can easily be mapped to instances of Throwable then this is a good candidate.

Your first example would then translate to:

val a = Try("hey")
val b = Try(2)
val c = Try(sys.error("oops"))

for{
 x <- a
 y <- b
 z <- c
} yield(User(x,y,z))

To provide a default value, just use orElse:

val a = Try("hey")
val b = Try(2)
val c = Try(sys.error("oops"))

for{
 x <- a
 y <- b
 z <- c.orElse(Try("default String"))
} yield(User(x,y,z))
Régis Jean-Gilles
  • 32,541
  • 5
  • 83
  • 97
0

A possible workaround is to consider that you can skip the for-comprehension for the last variable:

val z = c.fold(Function.const("default string"), identity)

or

val z = c.right.getOrElse("default string")

and then:

for{
 x <- a.right
 y <- b.right
} yield(User(x,y,z))
Nicolas
  • 24,509
  • 5
  • 60
  • 66