Per the title, there are a couple of reasonable and idiomatic ways that I know of to return the first successful computation, though I'm most interested here in how to handle the case when we want to know the specific failure of the last attempt when all attempts fail. As a first attempt, we can use collectFirst
and do something like the following:
def main(args: Array[String]) {
val xs = (1 to 5)
def check(i: Int): Try[Int] = {
println(s"checking: $i")
Try(if (i < 3) throw new RuntimeException(s"small: $i") else i)
}
val z = xs.collectFirst { i => check(i) match { case s @ Success(x) => s } }
println(s"final val: $z")
}
This seems like a reasonable solution if we don't care about the failures (actually, since we're always returning a success, we never return a Failure
, only a None
in the case there is no successful computation).
On the other hand, to handle the case when all attempts fail, we can capture the last failure by using the following:
def main2(args: Array[String]) {
val xs = (1 to 5)
def check(i: Int): Try[Int] = {
println(s"checking: $i")
Try(if (i < 3) throw new RuntimeException(s"small: $i") else i)
}
val empty: Try[Int] = Failure(new RuntimeException("empty"))
val z = xs.foldLeft(empty)((e, i) => e.recoverWith { case _ => check(i) })
println(s"final val: $z")
}
The disadvantages here are that you create a "fake" Throwable
representing empty, and if the list is very long, we iterate over the whole list, even though we may have succeeded very early on, even if later iterations are essentially no-ops.
Is there a better way to implement main2
that is idiomatic and doesn't suffer from the aforementioned disadvantages?