Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to connect two Scala Futures

Tags:

scala

future

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?

like image 681
user3593261 Avatar asked Dec 17 '22 19:12

user3593261


2 Answers

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)
   }
like image 116
Dima Avatar answered Jan 08 '23 00:01

Dima


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
}
like image 22
Andrey Tyukin Avatar answered Jan 08 '23 02:01

Andrey Tyukin