mthmulders
mthmulders

Reputation: 9705

Convert a scala.util.Try to a scala.concurrent.Future

I started migrating my Scala 2.11 code base to Scala 2.12. In my application I had a method that looked like this:

Future {
  someMethodReturningTry()
} onSuccess {
  case Success(result) => processResult()
  case Failure(reason) => log.error(s"Couldn't do it: ${reason.getMessage}")
}

Now, if you compile this with Scala 2.12, you'll get:

method onSuccess in trait Future is deprecated (since 2.12.0): use foreach or onComplete instead (keep in mind that they take total rather than partial functions)

So I started exploring how I could solve this in an elegant way.

The someMethodReturningTry() method really should return a Try[], since it involves parsing some text structure and that might fail, so I prefer to keep the return type for that method the same.

The best I could think of is

Future {
  someMethodReturningTry()
} flatMap {
  case Success(result) => Future.successful(result)
  case Failure(reason) => Future.failed(reason)
} onComplete {
  case Success(result) => processResult()
  case Failure(reason) => log.error(s"Couldn't do it: ${reason.getMessage}")
}

But that feels a bit redundant: creating a Future just to model the fact that something in the future (which already is captured inside a Future) went well.

This approach creates an extra Future which I hope to get rid of, but I can't figure out how. Any suggestions?

Upvotes: 1

Views: 954

Answers (2)

jwvh
jwvh

Reputation: 51271

It's not clear to me why you don't just...

Future {
  someMethodReturningTry() match {
    case Success(result) => processResult(result)
    case Failure(reason) => log.error(s"Couldn't do it: ${reason.getMessage}")
  }
}

You're free to handle, or ignore, the Future failure separately.

Upvotes: 3

Jeffrey Chung
Jeffrey Chung

Reputation: 19527

You could adjust your pattern matching in the following way:

Future {
  someMethodReturningTry()
} onComplete {
  case Success(Success(result)) => processResult()
  case Success(Failure(reason)) => log.error(s"Couldn't do it: ${reason.getMessage}")
  case Failure(reason) =>
    log.error(s"The future failed: ${reason.getMessage}")
    // or do nothing
}

Note that the onSuccess callback is executed only if the Future succeeds, so your original code didn't do anything if the Future contained an exception. If that is your intent, you can leave the case Failure(reason) => clause above blank (but it's probably more helpful to retain the error logging as shown).

Upvotes: 2

Related Questions