Scala Option[Future[T]] to Future[Option[T]]
Asked Answered
O

7

29

How can I convert Option[Future[T]] to Future[Option[T]] in scala?

I want to use it in:

val customerAddresses = for {
  a <- addressDAO.insert(ca.address) // Future[Address]
  ia <- ca.invoiceAddress.map(addressDAO.insert) // Option[Future[Address]]
} yield (a, ia) // Invalid value have to be two futures

Here signature insert method

def insert(address: Address): Future[Address]

ca is a CustomerData

case class CustomerData(address: Address, invoiceAddress: Option[Address])
One answered 6/7, 2016 at 14:12 Comment(4)
So a <- addressDAO.insert(ca.address) returns a Future[Option] ?Distend
Could you specify signatures of your methods?Began
I'd use .sequence from scalaz/cats with github.com/milessabin/si2712fix-plugin - but that would probably be overkill.Plumbic
Public note to future self: every time I try cats's .sequence for this it doesn't work until I realize there are conflicting implicits in scope, so if you move import cats.implicits._ directly above the call to .sequence then it works.Sporting
P
36
import scala.concurrent.Future
import scala.concurrent.ExecutionContext

def f[A](x: Option[Future[A]])(implicit ec: ExecutionContext): Future[Option[A]] = 
  x match {
     case Some(f) => f.map(Some(_))
     case None    => Future.successful(None)
  }

Examples:

scala> f[Int](Some(Future.successful(42)))
res3: scala.concurrent.Future[Option[Int]] = Success(Some(42))

scala> f[Int](None)
res4: scala.concurrent.Future[Option[Int]] = scala.concurrent.impl.Promise$KeptPromise@c88a337
Pillsbury answered 6/7, 2016 at 14:31 Comment(0)
G
16

If you have cats as a dependency in your application, the most beautiful way would be to use traverse

import cats._
import cats.implicits._

val customerAddresses = for {
  a  <- addressDAO.insert(ca.address)                 // Future[Address]
  ia <- ca.invoiceAddress.traverse(addressDAO.insert) // Future[Option[Address]]
} yield (a, ia)
Gastronome answered 27/9, 2018 at 5:35 Comment(2)
this is gorgeousAlow
you save my dayDominicdominica
N
12

The standard library does provide the methods to use Future.sequence on an Option, unfortunately you have to plumb them together.

Either as a quick method:

def swap[M](x: Option[Future[M]]): Future[Option[M]] =
    Future.sequence(Option.option2Iterable(x)).map(_.headOption)

Note I found the implicit Option.option2Iterable was already in scope for me. So you may not need to provide it, reducing the code down to Future.sequence(x).map(_.headOption)

Or you may prefer an extension method:

implicit class OptionSwitch[A](f: Option[Future[A]]) {
    import scala.concurrent.Future

    def switch: Future[Option[A]] = Future.sequence(Option.option2Iterable(f))
      .map(_.headOption)
  }


val myOpt = Option(Future(3))
myOpt.switch
Naturalism answered 7/7, 2017 at 11:32 Comment(0)
J
9

Here is another solution:

def swap[T](o: Option[Future[T]]): Future[Option[T]] =
  o.map(_.map(Some(_))).getOrElse(Future.successful(None))

The trick is to convert Option[Future[T]] into Option[Future[Option[T]]] which is easy, and then extract the value from that Option.

Jackson answered 28/9, 2017 at 15:33 Comment(0)
I
1

When you have a list (or any TraversableOnce) of futures and want a single future for computing the whole list, you use Future.sequence or Future.traverse. You can think of an Option like a list of 1 or 0 elements but since is technically not a list you have to go for a little conversion in this case. Anyway, this is a code that does it normally:

  val optionFuture:Option[Future[String]] = ???

  val futureOption:Future[Option[String]] = Future.sequence(optionFuture.toIterable).map(_.headOption)

In you example use better Future.traverse:

  val customerAddresses = for {
    a <- addressDAO.insert(ca.address) // Future[Address]
    ia <- Future.traverse(ca.invoiceAddress.toIterable)(addressDAO.insert).map(_.headOption) // Future[Option[Address]]
  } yield CustomerData(a, ia) // Types OK
Igloo answered 12/11, 2018 at 3:19 Comment(0)
R
0
val customerAddresses = for {
  a <- addressDAO.insert(ca.address) // Future[Address]
  ia <- ca.invoiceAddress.map(x => addressDAO.insert(x).map(_.map(k => Some(k))).getOrElse(Future.successful(None)))
} yield (a, ia)
Resent answered 28/9, 2018 at 6:0 Comment(0)
C
0

You can do it simply using Dumonad

import io.github.dumonad.dumonad.Implicits._
futureOfOption.dummed
Chilton answered 20/7, 2021 at 17:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.