purpleRaincoat
purpleRaincoat

Reputation: 3

If the first statement in a for comprehension fails , recover is not able to catch the exception

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

Answers (1)

Andrey Tyukin
Andrey Tyukin

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 Trys 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 recovers 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

Related Questions