Reputation: 2218
For example, let say I have a function
def foo(): Either[String, Int] = ???
I want to call this function 3 times. If all the values are Right
, I want the sum. If I have one Left
, I want to get the error and the sum of all previous Right
values (~ stop the computation at this point).
the only way I found to do this is :
List(foo, foo, foo).foldLeft((None, 0)) {
case ((Some(err), sum), _) => (Some(err), sum)
case ((None, sum), fn) => fn() match {
case Left(err) => (Some(err), sum)
case Right(x) => (None, sum + x)
}
}
Is there some generic functional programming feature (with cats or scalaz for example) to do that?
Upvotes: 3
Views: 436
Reputation: 7353
Using Stream.span
:
val (ints, rest) = Stream.continually(foo()).take(3).span(_.isRight)
val sum = ints.map(_.right.get).sum
val error = rest.headOption
Upvotes: 3
Reputation: 6385
Pretty much the same solution with tail recursive function:
def sumRights(allFoos: List[() => Either[String, Int]], accum: Int = 0): (Option[String], Int) = {
allFoos match {
case Nil => (None, accum)
case head :: tail =>
head() match {
case Right(x) => sumRights(tail, accum + x)
case Left(err) => (Some(err), accum)
}
}
Another imperative solution is to split on a first Left with splitWhere
, and sum up all values of first output list.
Upvotes: 0
Reputation: 10882
I'm not sure if scalaz of cats have something specifically for this, but here is how you can solve this in vanilla scala:
lazy Stream:
Stream.continually(foo()).take(3) match {
case s =>
val err = s.collectFirst {
case Left(err) => err
}
val sum = s.takeWhile(_.isRight)
.flatMap(_.right.toOption)
.sum
(err, sum)
}
Stream
will be calculated lazily and cached.
Tail recursion:
def rec(sum: Int = 0, i: Int = 2): (Option[String], Int) =
if (i < 0) (None, sum) else foo() match {
case Left(e) => (Some(e), sum)
case Right(s) => rec(s + sum, i - 1)
}
Upvotes: 0