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?
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)
}
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With