Reputation: 8821
I really like scala.util.Try
in Scala 2.10, and how it works with for-comprehension makes handling multiple steps that could go wrong easily.
For example, we could use the following code to make sure we only print out that two numbers if and only if everything is under control and we get value correctly.
def tryA: Try[Int] = {....}
def tryB: Try[Int] = {....}
for {
a <- tryA
b <- tryB
} {
println (s"We got:${a+b}")
}
But one of my concern is that this code is actually ignore any exceptions, which means it will looks like the following try-cactch block:
try {
// .....
} catch {
case _: Exception => // Swallow any exception
}
As far as I know, there is an argument that this kind of codes is a bad smell, because no one will notice there is an exception occurs.
What I would like to achieve is that still using for
to make sure the println
only execute if everything is OK, but if there is any exception in any steps, it will blow up and throw out the exception directly.
Currently here is how I do this, but it seems less elegant because it introduce a new Try[Unit]
object, so I'm wondering how could I make this code better?
For example, is it possible to get rid of the result
variable and result.get
statement, but still get exception being thrown?
def tryA: Try[Int] = {....}
def tryB: Try[Int] = {....}
val result = for {
a <- tryA
b <- tryB
} yield {
println (s"We got:${a+b}")
}
result.get
To make thing more clear, it is the result from Scala REPL of the first code in this question.
scala> def tryA: Try[Int] = Success(1)
tryA: scala.util.Try[Int]
scala> def tryB: Try[Int] = Failure(new Exception("error"))
tryB: scala.util.Try[Int]
scala> for {
| a <- tryA
| b <- tryB
| } {
| println (s"We got:${a+b}")
| }
scala>
We can see that nothing happens here, even tryB
is a Failure
with exception. What I would like to get is an exception being thrown, and without the introduce the new Try[Unit]
object with yield
, is this possible?
Upvotes: 8
Views: 6788
Reputation: 8821
OK, I forgot we always has implicit conversion in Scala. ;-)
So we could implement this behaviour ourself, it will create more object than the yield
version, but I think the intention of this code is much more clear.
implicit class BlowUpTry[T](current: Try[T]) {
def throwIfFailed: Try[T] = current match {
case Success(value) => current
case Failure(exception) => throw exception
}
}
def tryA: Try[Int] = Success(1)
def tryB: Try[Int] = Failure(new Exception("error"))
for {
a <- tryA.throwIfFailed
b <- tryB.throwIfFailed
} {
println(s"We got ${a + b}")
}
Upvotes: 0
Reputation: 26486
You can use recover
:
import scala.util.Try
def tryEven = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i }
def tryEvenOrNeg1 = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i } recover { case exx: Exception => -1 }
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res1: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res2: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res3: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
Got 542, -1
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res5: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res6: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
Got 692, 750
I removed the resNN
that reflected Success(())
.
Upvotes: 4