Question

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.

Was it helpful?

Solution

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

OTHER TIPS

Equivalent code for

for {
   opt <- res
   r <- opt
} yield (print(!r))

is not

res.map(opt => opt.map(r => print(!r)))

but

res.flatMap(opt => opt.map(r => print(!r)))

And it makes no sense in this case.

For the chain of maps you could use nested for-comprehensions

for { opt <- res }
  for { r <- opt }
    print(!r)

But map looks better.

Well, for comprehensions are deceiving in the way they look. Your for-comprehension expands to:

res.flatMap(opt => opt.map(r => print(!r))

Which is obviously wrong as flatMap expects a return type of Future[T] and you are providing Option[Unit]

Though sometimes, for code tidiness you would want to have a single for loop with many such expressions. In those cases you could:

 scala> implicit def toFuture[T](t: => T):Future[T] = {
     | val p = Promise[T]()
     | p.tryComplete(Try(t))
     | p.future
     | }

scala>  for {
     |          opt <- res
     |          r <- opt
     |       }  yield {print(!r)}

false

The above produces:

res.flatMap[Option[Unit]](((opt: Option[Boolean]) => 
            toFuture[Option[Unit]](opt.map[Unit](((r: Boolean) => print(!r))))))

Edit: Though you need to take all the pain if you are using yield. If you do not wish to use for comprehension as an expression, then you can do as you desired:

scala> val i = Future(Some(true))
i: scala.concurrent.Future[Some[Boolean]] = scala.concurrent.impl.Promise$DefaultPromise@6b24a494

scala>   val j = Option(1)
j: Option[Int] = Some(1)

scala>   val k = Right(1).right
k: scala.util.Either.RightProjection[Nothing,Int] = RightProjection(Right(1))

scala>   
     |   for{
     |     x <- i
     |     y <- j
     |     z <- k
     |   }{
     |     println(i,j,k)
     |   }

(scala.concurrent.impl.Promise$DefaultPromise@6b24a494,Some(1),RightProjection(Right(1)))

This way no implicit is required. As compiler uses foreach at every junction. -Xprint:typer gives:

i.foreach[Unit](((x: Option[Boolean]) => 
     j.foreach[Any](((y: Int) => 
         k.foreach[Unit](((z: Int) =>    println(scala.this.Tuple3.apply[scala.concurrent.Future[Option[Boolean]], Option[Int], Either.RightProjection[Nothing,Int]](i, j, k))))))))
  }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top