Suppose I have:
val res:Future[Option[Boolean]] = Future(Some(true))
and I can do:
res.map(opt => opt.map(r => print(!r)))
I guess the for comprehension for this would be:
for {
opt <- res
r <- opt
} yield (print(!r))
but this does not work! I get an error ie:
error: type mismatch;
found : Option[Unit]
required: scala.concurrent.Future[?]
r <- opt
How do I use a Future[Option[Boolean]] in a for comprehension to extract or convert the Boolean?
Note: this is a simplification of the problem I have at the moment with many Future[Option[Boolean]] variables that I would like to use together in a for comprehension.
A for-comprehension really makes this look like it should all work, doesn't it? But let's think about what you're asking for.
First, note that for
un-nests:
for {xs <- List(List(5)); x <- xs} yield x
produces
List(5)
Now, without even getting into the type signatures or desugaring, we can think about replacing List
with some arbitrary type T
, and we'll call the contained type A
:
for { xs <- T(T(a: A)); x <- xs } yield x
and we should get a
T[A]
back (presumably the one we put in, but the types don't actually promise us that).
Okay, but what about
for { xs <- T(U(a: A)); x <- xs } yield x
? This is strictly more general than the case where the two things have the same nesting. Well, if T
and U
both have a common supertype S
, then we can just view the whole thing as S(S(a: A))
, so we'll at least get an S
back. But what about in the general case?
The bottom line is that it depends. For example, let's consider the case where T=Future
, U=Option
. We have the following possibilities:
Success(Some(a))
Success(None)
Failure(t: Throwable)
Now, can we come up with any coherent policy for unwrapping? If we unwrap into a Future
, then what A
do you use for the Success(None)
case? You don't have one available to return. Likewise, if you try to vanquish the outer Future
, how do you know, without explicitly stating it somehow to the compiler, that Failure
should be mapped to None
(if indeed it should--maybe it should go to a default!).
So the bottom line is that you just can't do this correctly in general without specifying what is supposed to happen for every pair T[U[_]]
. (I encourage the interested reader to peruse tutorials on monads and monad transformers.)
There is a way out, though: if you can explicitly turn your U
into a T
, or your T
into your U
, you can take advantage of the unwrapping capability. It's pretty easy to turn an Option
into a Future
, so the easiest solution is
for { opt <- res; r <- Future(opt.get) } yield r
(where just let the exception get thrown on none.get
). Alternatively, you can turn the Future
into an Option
with the slightly ugly
for { opt <- res.value.flatMap(_.toOption); r <- opt } yield r