0

I have a list of Eithers

val list: List[Either[String, Int]] = List(Right(5), Left("abc"), Right(42))

As a result I want a Right if everything in the list is a Right else I want a Left. This sounds like the list should be biased (e.g. use Try instead) but let's assume it isn't or shouldn't be.

The content of the resulting Right or Left will always be the same (e.g. a string, see blow) - only the Container shall differ. E.g. with the list above we want to create a string from this list so the result should be of a Left like Left("Right(5) -> Left(abc) -> Right(42)"). If we had another Right(12) instead of the Left("abc") it should be Right("Right(5) -> Right(12) -> Right(42)").

I could manually check if there is at least one Left in the list and branch with an if to create a Left or a Right as result, but I wonder: is there a more Scala-like way to do it?

valenterry
  • 757
  • 6
  • 21

2 Answers2

0

You can achieve that in a functional way with a foldLeft.

Basically in the fold function you fold using the rules

Right + Right => Right
SomethingElse => Left

In scala code:

def string(a: Any, b: Any): String = if (a.toString.isEmpty)
   s"${b.toString}" else s"${a.toString} -> ${b.toString}"

def transform[T, U](list: List[Either[T, U]]) = 
  list.foldLeft[Either[String, String]](Right("")) {
    case (Right(a), bb @ Right(b)) => Right(string(a, bb))
    case (Right(a), bb) => Left(string(a, bb))
    case (Left(a), bb) => Left(string(a, bb))
  }

transform(List(Right(5), Left("abc"), Right(42)))
// prints Left(Right(5) -> Left(abc) -> Right(42))
transform(List(Right(5), Right("abc"), Right(42)))
// prints Right(Right(5) -> Right(abc) -> Right(42))
Nicolò Martini
  • 5,182
  • 4
  • 32
  • 38
0

Either is actually used in sence of Value or Error in Haskell. But scala Either's design does not allow it's use as "Error Monad". That was fixed in scalaz library via \/ type. Thing you want to basically implemented via sequence extension of Traverse types. So prefix your code with:

import scalaz._
import Scalaz._

and get your result like

type Error[A] = String \/ A
val mlist = (list map \/.fromEither).sequence[Error, Int] 

or even oneline type currying:

val mlist = (list map \/.fromEither).sequence[({type E[A] = String \/ A})#E, Int]
Odomontois
  • 15,918
  • 2
  • 36
  • 71