38

What's the cleanest way to map the Exception of a failed Future in scala?

Say I have:

import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global

val f = Future { 
  if(math.random < 0.5) 1 else throw new Exception("Oh no") 
}

If the Future succeeds with 1, I'd like to keep that, however if it fails I would like to change the Exception to a different Exception.

The best I could come up with is transform, however that requires me to make a needless function for the success case:

val f2 = f.transform(s => s, cause => new Exception("Something went wrong", cause))

Is there any reason there is no mapFailure(PartialFunction[Throwable,Throwable])?

theon
  • 14,170
  • 5
  • 51
  • 74
  • 8
    transform is the right way to go. you don't need to create the s => s function, just pass in ``identity`` – Viktor Klang Aug 15 '13 at 12:02
  • Cheers. I didn't know about the `identity` function. I'm sure there will be more times where that will come in handy. – theon Aug 15 '13 at 21:06

2 Answers2

43

There is also:

f recover { case cause => throw new Exception("Something went wrong", cause) }

Since Scala 2.12 you can do:

f transform {
  case s @ Success(_) => s
  case Failure(cause) => Failure(new Exception("Something went wrong", cause))
}

or

f transform { _.transform(Success(_), cause => Failure(new Exception("Something went wrong", cause)))}
Viktor Klang
  • 26,479
  • 7
  • 51
  • 68
  • 2
    While this is a very clear syntax, we still have to `throw` the new exception, instead of getting to map `Throwable` to `Throwable`. Is there a combinator for just that? – owensmartin May 10 '17 at 00:04
17

You could try recoverWith as in:

f recoverWith{
  case ex:Exception => Future.failed(new Exception("foo", ex))
}
cmbaxter
  • 35,283
  • 4
  • 86
  • 95
  • Future.failed is not evaluated on an ExeecutionContext: http://www.scala-lang.org/api/current/#scala.concurrent.Future$ see ``failed`` – Viktor Klang Aug 15 '13 at 12:40
  • @ViktorKlang, I must be confused then. I had made this assumption in looking at the `impl.KeptPromise` class' `onComplete` method. In there, there are the two lines `val preparedEC = executor.prepare; (new CallbackRunnable(preparedEC, func)).executeWithValue(completedAs)`. I assumed this to imply that even though we were giving an explicit value that it still for some reason was hitting the executor. It always seemed strange to me and I guess it's actually because I was misreading it. Thanks for the heads up. – cmbaxter Aug 15 '13 at 12:57
  • Thanks for the answers. My only hesitation with using `recover`/`recoverWith` is that when I see it I immediately assume that the intention is to recover from a failure with a successful result. Maybe that is just me. Even so they are good alternatives, thanks! – theon Aug 15 '13 at 21:23
  • @cmbaxter I still do not understand on how it doesn't hit ExecutionContext. – Jatin Aug 06 '14 at 05:53
  • @cmbaxter - could you please check out my related question - http://stackoverflow.com/questions/31173982/dealing-with-a-failed-future. – Kevin Meredith Jul 02 '15 at 01:03
  • @theon The fact that you use an exception or something else to signal to your program that something failed should not matter i think. Hence using recover or recoverwith should be the way to go i presume. – MaatDeamon Oct 13 '15 at 22:26
  • @MattDeomon Since it is a failure, I'd rather use an Exception, so it triggers `onFailure` callbacks rather than `onSuccess` callbacks. I think it is more intuitive that way. I still also find `transform` more intuitive than `recover` or `recoverWith`, because I am not recovering from a failure with a success, I am transforming a failure into another failure. That is true regardless of whether we use an `Exception` or a regular class to signal the failure. – theon Nov 18 '15 at 20:37