ps0604
ps0604

Reputation: 1071

Try Failure is not catching exceptions

In the code below I have two Play for Scala functions, the first one catches an exception (this works fine) and in the second one I'm trying to rewrite it using Try.

I have two problems with Try: (1) when the number is negative the method doesn't fail, (2) I need to wrap all the responses with Future.successful.

How to fix this code?

  class Test extends Controller  {

    def test1 = Action.async { request =>

         val future = isPositive(-1)
         future.map { result =>
             Ok("OK, it's positive")
         }
         .recover { 
             case e => Ok(e.getMessage)
         }
    }

    def isPositive(i: Int) = Future {
        if (i<0)
            throw new Exception ( "Number is negative" )
        else
            i
    }

    def test2 = Action.async { request =>
        isPositiveWithTry(-1) match {
            case Success(s) => Future.successful(Ok("OK, it's positive (Try succeded)"))
            case Failure(f) => Future.successful(Ok(f.getMessage + " (Try failed)"))
        }
    }

    def isPositiveWithTry(i: Int) : Try[Future[Int]] = Try {
         isPositive(i)
    }

 }

Upvotes: 0

Views: 179

Answers (2)

Nagarjuna Pamu
Nagarjuna Pamu

Reputation: 14825

In isPositive method exceptions are already caught by Future

def isPositive(i: Int) = Future {
        if (i<0)
            throw new Exception ( "Number is negative" )
        else
            i
}

In the below code

def isPositiveWithTry(i: Int) : Try[Future[Int]] = Try {
     isPositive(i)
} 

isPositive already catches all expections and Try is always a success.

So, when i is negative. Exception raised are handled by future and try gets a success value, resultant Try is a success. So you get successful Try with a failed Future inside.

Understanding using Grenade example

Assume throwing the exception as blowing up a Grenade.

Assume Future and Try as two layers. When grenade is blasted inside the double layer of Try[Future] i.e Try is around Future and grenade is gone off in the Future.

Now Future withstands the blast and becomes a failed value. As Future already took the damage caused by the damage of exception(grenade). Try will be a success but the value inside the Try is a failed future value. That failed future value is nothing but the exception raised.

Try is redundant when you are using Future

You can refactor your code to below one

Get rid of isPositiveWithTry. This method is not needed.

 def isPositive(i: Int) = Future {
    if (i<0)
        throw new Exception ( "Number is negative" )
    else
        i
 }

def test2 = Action.async { request =>
    isPositive(-1).flatMap { _ =>
       Future.successful(Ok("OK, it's positive (Try succeded)"))
    }.recoverWith {
        case f: Throwable => Future.successful(Ok(f.getMessage + " (Try failed)"))
    }
}

Again test2 can also be written as

 def test2 = Action.async { request =>
    isPositive(-1).map { _ =>
       Ok("OK, it's positive (Try succeded)")
    }.recover {
        case f: Throwable => Ok(f.getMessage + " (Try failed)")
    }
 }

In case isPositive returns Try

def isPositive(i: Int) = Try {
        if (i<0)
            throw new Exception ( "Number is negative" )
        else
            i
}

Now test2 will look like

def test2 = Action.async { request =>
        isPositive(-1) match {
            case Success(s) => Future.successful(Ok("OK, it's positive (Try succeded)"))
            case Failure(f) => Future.successful(Ok(f.getMessage + " (Try failed)"))
        }
    }

Upvotes: 1

wheaties
wheaties

Reputation: 35970

Couple points:

1) You need to rewrite your isPositive such that it does not surround itself via a Future. The Future is catching the exception.

def isPositive(i: Int) ={
  if (i<0)
    throw new Exception ( "Number is negative" )
  else
    i
}

2) If you have a Try and you want a Future, then you can use the method on the companion object of Future, Future.fromTry. That will take a Try and turn it into the correct state of a Future.

Upvotes: 1

Related Questions