This isn't necessarily the best solution, but it's what I've come up with.
I started by trying to find a common type to work with
type N[X, Y] = Option[X] => Future[Option[Y]]
...then converting f1
, f2
, and f3
to that common type.
val f1: (A => Option[B]) = ???
val f1N: N[A, B] = {
case None => Future.successful(None)
case Some(a) => Future.successful(f1(a))
}
val f2: (B => Future[Option[C]]) = ???
val f2N: N[B, C] = {
case None => Future.successful(None)
case Some(b) => f2(b)
}
val f3: C => D = ???
val f3N: N[C, D] = {
case None => Future.successful(None)
case Some(c) => Future.successful(Some(f3(c)))
}
Now that I've created f1N
, f2N
, and f3N
, I can use them in a nice-looking for-comprehension.
val y: Future[Option[D]] = for {
aOpt <- x
bOpt <- f1N(aOpt)
cOpt <- f2N(bOpt)
dOpt <- f3N(cOpt)
} yield dOpt