Reputation: 3
I am struggling to understand the for comprehension and exception handling in Scala.
If the first statement in a for comprehension fails , recover is not able to catch the exception.
Code where recover catches the exception successfully(Exception thrown in 2nd statement):
import scala.util.{Success, Try}
object ExceptionThrownIn2ndStatement {
def failTryUnit(x: Unit): Try[Int] = {
println(x)
val a = 1 / 0
new Success(a)
}
def main(args: Array[String]): Unit = {
(for {
var0 <- Try(println("Zeroth function"))
varFailure <- failTryUnit(var0) //exception thrown here
var1 <- Try(println("first function", varFailure))
} yield var1) recover { case e =>
println("Exception caught", e) //exception caught here
}
}
}
Output :
Zeroth function
()
(Exception caught,java.lang.ArithmeticException: / by zero)
Code where recover does NOT catch the exception successfully :
import scala.util.{Success, Try}
object ExceptionThrownIn1stStatement {
def failTryUnit(x: Unit): Try[Int] = {
println(x)
val a = 1 / 0
new Success(a)
}
def main(args: Array[String]): Unit = {
(for {
varFailure <- failTryUnit({}) //exception thrown here
var0 <- Try(println("zeroth function", varFailure))
var1 <- Try(println("first function", var0))
} yield var1) recover { case e =>
println("Exception caught",e) //Exception does not get caught here
}
}
}
Output:
()
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ExceptionThrownIn1stStatement$.failTryUnit(ExceptionThrownIn1stStatement.scala:6)
at ExceptionThrownIn1stStatement$.main(ExceptionThrownIn1stStatement.scala:12)
at ExceptionThrownIn1stStatement.main(ExceptionThrownIn1stStatement.scala)
Edit : I understand that this is not the way recover is supposed to be used. I am just confused as to why this happens. Please help me understand this. I am new to Scala.
Upvotes: 0
Views: 256
Reputation: 44918
Here is a shorter example that demonstrates the same behavior:
Success(42).flatMap(x => { assert(false); Success(x + 58) })
vs.
{ assert(false); Success(42) }.flatMap(x => Success(x + 58))
The first one will return a Failure
with a caught error. The second one will crash with an AssertionError
.
The first returns a Failure
because that's the semantics of Try
s flatMap
- it catches all exceptions that occur during the execution of the function passed to it.
The second one crashes immediately, because the very first statement is an assert(false)
, so you never get to the point where you construct a Try
in the first place, the AssertionError
is thrown before the first Success
constructor is invoked. It wouldn't matter whether you append more recover
s on it or not - no Try
will ever be instantiated in this program.
Here is what you would have to do to catch the exception occurring during the very first calculation (42
):
Try { assert(false); 42 }.flatMap(x => Success(x + 58))
In your code, that would be
def failTryUnit(x: Unit): Try[Int] = Try {
println(x)
1 / 0
}
Upvotes: 1