Scala recover or recoverWith

53,611

Solution 1

Well, the answer is clearly described in scaladocs:

  /** Creates a new future that will handle any matching throwable that this
   *  future might contain. If there is no match, or if this future contains
   *  a valid result then the new future will contain the same.
   *
   *  Example:
   *
   *  {{{
   *  Future (6 / 0) recover { case e: ArithmeticException => 0 } // result: 0
   *  Future (6 / 0) recover { case e: NotFoundException   => 0 } // result: exception
   *  Future (6 / 2) recover { case e: ArithmeticException => 0 } // result: 3
   *  }}}
   */
  def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = {

  /** Creates a new future that will handle any matching throwable that this
   *  future might contain by assigning it a value of another future.
   *
   *  If there is no match, or if this future contains
   *  a valid result then the new future will contain the same result.
   *
   *  Example:
   *
   *  {{{
   *  val f = Future { Int.MaxValue }
   *  Future (6 / 0) recoverWith { case e: ArithmeticException => f } // result: Int.MaxValue
   *  }}}
   */
  def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] = {

recover wraps plain result in Future for you (analogue of map), while recoverWith expects Future as the result (analogue of flatMap).

So, here is rule of thumb:

If you recover with something that already returns Future, use recoverWith, otherwise use recover.

update In your case, using recover is preferred, as it wraps the exception in Future for you. Otherwise there is no performance gain or anything, so you just avoid some boilerplate.

Solution 2

Using recoverWith you are asked to return a wrapped future, using recover you are asked to throw an exception.

.recoverWith # => Future.failed(t)
.recover # => throw t

I prefer using recoverWith because I think functional programming prefers returning objects than throwing exceptions which is less of a functional style, even if it's internal code block, I think it still holds..

However if I have an internal piece of code in the recovery block which might throw an exception then, in that case, rather than catching it and wrapping it with Future, or Trying it, I might as well just run this piece of code in combination with recover and it would handle the exception wrapping for you, which would make the code more readable and compact.

Share:
53,611
Henrique dos Santos Goulart
Author by

Henrique dos Santos Goulart

Updated on May 27, 2020

Comments

  • Henrique dos Santos Goulart
    Henrique dos Santos Goulart about 4 years

    We are developing some systems in our company in Scala and we have some doubts. We were discussing about how to map the future exceptions and we don't know when we should use the option 1 or the option 2.

    val created: Future[...] = ???
    

    Option 1:

    val a = created recover {   
      case e: database.ADBException =>
        logger.error("Failed ...", e)
        throw new business.ABusinessException("Failed ...", e) 
    }
    

    Option 2:

    val a = created recoverWith {   
      case e: database.ADBException =>
        logger.error("Failed ...", e)
        Future.failed(new business.ABusinessException("Failed ...", e))
    }
    

    Could someone explain when should i do the option 1 or the option 2? What is the diff?

    • Azuaron
      Azuaron almost 5 years
      To be somewhat pedantic 3 years later for others who find this question, generally speaking the point of "recover" and "recoverWith" is not to simply transform your exceptions from one type to another, but to recover from the failure by performing the task in a different way so that you no longer have a failure. In that case, your use of "recover" vs "recoverWith" is dependent on whether the new recovery task happens inside a Future or without a Future.
    • hansvb
      hansvb over 4 years
      @Azuaron So if you simply need to transform exceptions, what do you recommend to use instead of recover? stackoverflow.com/q/59099000/14955
    • Azuaron
      Azuaron over 4 years
      recover is still pretty much the only option, unfortunately, but it's important to understand what you're taking advantage of.
    • Jus12
      Jus12 over 4 years
      @Thilo there is transform to transform the exception.