Using a for comprehension, I can succinctly make a series of interdependent calls to one or more functions that return an Either
, short circuiting when any returns a Left
.
I can use this approach to call a single function with different arguments:
def getValue(value: Int) = value match {
case 9 => Left("Invalid")
case _ => Right(value)
}
// Right((1,2,8))
val values1 = for {
a <- getValue(1)
b <- getValue(2)
c <- getValue(8)
} yield (a, b, c)
// Left(Invalid)
val values2 = for {
a <- getValue(1)
b <- getValue(2)
c <- getValue(9)
} yield (a, b, c)
Let's say I have a sequence of values, e.g. Seq(1, 2, 9)
. Is there a way to write a similar for comprehension to achieve the same result?
For example, the following achieves the same result, though it doesn't short circuit, and it doesn't use a for comprehension:
val inputs = Seq(1, 2, 9)
val values3 = inputs.map(getValue).partitionMap(identity) match {
case (Nil, v) => Right(v)
case (first :: _, _) => Left(first)
}
You could use foldLeft
to traverse the inputs, and then use for comprehension inside foldLeft function.
Since type of acc
is Either
, when we use for comprehension on it, it will make a short circuit.
val result: Either[String, Seq[Int]] =
inputs.foldLeft(Right(Seq.empty): Either[String, Seq[Int]]) { (acc, n) =>
for {
xs <- acc
x <- getValue(n)
} yield xs :+ x
}
You can add statement like _ = println(x)
to debug it.
scala-cli
Welcome to Scala 3.7.2 (21.0.2, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala> def getValue(value: Int) = value match {
| case 9 => Left("Invalid")
| case _ => Right(value)
| }
def getValue(value: Int): Either[String, Int]
scala> val inputs1 = List(1,2,9,4,1)
val inputs1: List[Int] = List(1, 2, 9, 4, 1)
scala> val inputs2 = List(1,2,8)
val inputs2: List[Int] = List(1, 2, 8)
scala> def result(inputs: Seq[Int]) =
| inputs.foldLeft(Right(Seq.empty): Either[String, Seq[Int]]) { (acc, n) =>
| for {
| xs <- acc
| x <- getValue(n)
| _ = println(x)
| } yield xs :+ x
| }
|
def result(inputs: Seq[Int]): Either[String, Seq[Int]]
scala> result(inputs1)
1
2
val res0: Either[String, Seq[Int]] = Left(Invalid)
scala> result(inputs2)
1
2
8
val res1: Either[String, Seq[Int]] = Right(List(1, 2, 8))