user3593261
user3593261

Reputation: 568

How to connect two Scala Futures

I have two Future functions:

def parseIntFuture(str: String) = Future{scala.util.Try(str.toInt).toOption}
def divideFuture(a: Int, b: Int) = Future{ if (b == 0) None else Some(a / b)}

And now I want connect them and eventually get a Future[Option[Int]] type result which is the second one's return value, but if I do like this:

def stringDivideBy(aStr: String, bStr: String) = {
    val x = for {
        aNum <- parseIntFuture(aStr)
        bNum <- parseIntFuture(bStr)
    } yield (aNum, bNum)

    x.map(n => {
        for{
            a <- n._1
            b <- n._2
        } yield divideFuture(a, b)
    })
}

Actually I will get Future[Option[Future[Option[Int]]]] instead of Future[Option[Int]] only. I know it's because I'm passing one Future to the other, but I don't know what is the correct way to connect these two Futures one by one avoiding using Await. I halt explicitly use Await, then what would be the solution?

Upvotes: 4

Views: 283

Answers (2)

Dima
Dima

Reputation: 40508

You don't need monad transformers and other "heavy artillery" for simple stuff like this. The general rule is don't make your code more complex than it absolutely has to be.

 (parseIntFuture(foo) zip parseIntFuture(bar))
   .flatMap {
     case (Some(a), Some(b)) => divideFuture(a, b) 
     case _ => Future.successful(None)
   }

Upvotes: 6

Andrey Tyukin
Andrey Tyukin

Reputation: 44992

There is this thing called OptionT monad transformer that solves exactly this problem. With OptionT, your code would look somewhat like

import cats.data.OptionT

// ...

val x = (for {
    aNum <- OptionT(parseIntFuture(aStr))
    bNum <- OptionT(parseIntFuture(bStr))
    res <- OptionT(divideFuture(aNum, bNum))
} yield res).value

and return a Future[Option[Int]].


You could avoid monad transformers at the cost of nested for-comprehensions:

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

def parseIntFuture(str: String) = Future{scala.util.Try(str.toInt).toOption}
def divideFuture(a: Int, b: Int) = Future{ if (b == 0) None else Some(a / b)}

def stringDivideBy(aStr: String, bStr: String): Future[Option[Int]] = {
  for {
    aOpt <- parseIntFuture(aStr)
    bOpt <- parseIntFuture(bStr)
    resOpt <- 
      (for {
        a <- aOpt
        b <- bOpt
      } yield divideFuture(a, b))
      .getOrElse(Future { None })
  } yield resOpt
}

Upvotes: 5

Related Questions