Function is returning `Future[Option[Boolean]]` how should i destructure it to check boolean?

Problem is suppose there is a function xyz() which has a return type of Future[Some[Boolean]] Now what i want is that I want to trigger a function abc() whenever this boolean value is true I am not sure how to destructure it in scala . What I have tried is

val ans = xyz()
ans.map((itr)=>{
      itr match{
          Some(true)=> abc()
   }
})

I could be completely wrong too , I don't know much about futures please recommend any easy to understand documentation for futures too and also please explain what is tecnique to destructure a future value a map function something or flatmap because I have seen them used frequently


Solution 1:

A Future represents a value that might not have been computed yet. Until there's a value, there's nothing to destructure.

The simplest, though non-deterministic, way to check the value of a Future is via the value method, which (in your case) would project it to an Option[Try[Option[Boolean]]], with values interpreted as:

  • None means that the future has not yet completed and thus has no value
  • Some(Failure(exception)) means that the future completed with a failure and the failure is elaborated as exception
  • Some(Success(None)) means the future completed with None
  • Some(Success(Some(true))) means the future completed with Some(true)
  • Some(Success(Some(false))) means the future completed with Some(false)

As noted, this is non-deterministic, though once the value transitions from None to Some, it will stay that way, so you could use a while-loop to check value.

That said, you'd get the same result, without wasting CPU, by blocking using Await.result(future, someDurationToWait). This will throw the exception if the future fails and otherwise you'd get an Option[Boolean] with the result.

That approach has the downside of blocking a thread, which is a finite resource and, done too often, can lead to deadlock. So you should really only be doing Await.result in the outermost layer of your program, or in places where you absolutely must present a blocking API.

The most idiomatic thing in Scala is to schedule callbacks to be executed in response to the future completing with a value. There are four main ways to schedule a callback, and eachof these requires that you specify an ExecutionContext in which the callback will be executed (selection of a proper ExecutionContext is outside of the scope of this answer):

  • filter: passes a failed future through unchanged and turns a succeeded future into a failed future unless some predicate is met.
val fut: Future[Option[Boolean]] = ???
// assuming that fut completes, transformed will only be failed or succeed with `Some(true)`
val transformed: Future[Option[Boolean]] =
  fut.filter(_.contains(true))
  • foreach: if the future is successful, will pass the result to a function called for side-effect:
val fut: Future[Option[Boolean]] = ???
// will print Some(false) or Some(true) or None if the future completes, otherwise nothing will ever print
fut.foreach(println _)
  • map: passes through a failed future and transforms the value of a successful future
val fut: Future[Option[Boolean]] = ???
def f(x: Option[Boolean]): Int =
  x match {
    case None => 0
    case Some(false) => 1
    case Some(true) => 2
  }

val transformed: Future[Int] = fut.map(f)
  • flatMap: lets you transform a future with transformation that results in another future
val fut: Future[Option[Boolean]] = ???
def callAnotherApi(): Future[Int] = ???

def f(x: Option[Boolean]): Future[Int] =
  x match {
    case None => Future.failed(new IllegalArgumentException("don't know"))
    case Some(false) => Future.successful(1)
    case Some(true) => callAnotherApi()
  }

fut.flatMap(f)

Solution 2:

I recommend getting familiar with matching syntax, or use some some IDE. There are several ways you can achieve this e.g.

ans.map {
  // case is necessary when you ar matching
  case Some(true) => abc()
  // handle all the cases, even if you need a wildcard
  case _ => // otherwise
}

They are obvious if you completed something like Coursera or other free tutorial.