8

假设我有:

val res:Future[Option[Boolean]] = Future(Some(true))

我可以这样做:

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

我想对此的理解将是:

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

但这不起作用!我收到一个错误,即:

error: type mismatch;
found   : Option[Unit]
required: scala.concurrent.Future[?]
r <- opt

如何在 for 理解中使用 Future[Option[Boolean]] 来提取或转换布尔值?

注意:这是我目前遇到的许多 Future[Option[Boolean]] 变量的简化,我想在理解中一起使用这些变量。

4

3 回答 3

12

理解确实使这看起来应该一切正常,不是吗?但是让我们考虑一下您的要求。

首先,请注意for取消嵌套:

for {xs <- List(List(5)); x <- xs} yield x

生产

List(5)

现在,甚至不需要进入类型签名或去糖,我们可以考虑List用一些任意类型替换T,我们将调用包含的类型A

for { xs <- T(T(a: A)); x <- xs } yield x

我们应该得到一个

T[A]

返回(大概是我们输入的那个,但类型实际上并没有向我们保证)。

好的,但是怎么样

for { xs <- T(U(a: A)); x <- xs } yield x

? 这比两个事物具有相同嵌套的情况更普遍。好吧,如果TU两者都有一个共同的超类型S,那么我们可以将整个事情视为S(S(a: A)),所以我们至少会得到一个S支持。但在一般情况下呢?

底线是它取决于。例如,让我们考虑 , 的T=Future情况U=Option。我们有以下几种可能:

Success(Some(a))
Success(None)
Failure(t: Throwable)

现在,我们能想出任何一致的解包策略吗?如果我们展开成 a Future,那么A你用什么来处理这个Success(None)案子?您没有可退货的。同样,如果您尝试征服 external Future,您怎么知道,而不以某种方式向编译器明确说明它Failure应该被映射到None(如果它确实应该 - 也许它应该成为默认值!)。

所以最重要的是,如果不指定每一对应该发生的事情,你通常不能T[U[_]]正确地做到这一点。(我鼓励感兴趣的读者仔细阅读有关monadmonad 转换器的教程。)

不过,有一条出路:如果您可以明确地将 yourU变成 aT或 yourT变成 your U,则可以利用展开功能。Option把 a变成 a很容易Future,所以最简单的解决方案是

for { opt <- res; r <- Future(opt.get) } yield r

(只要让异常被抛出none.get)。或者,您可以将Future带有Option稍微丑陋的

for { opt <- res.value.flatMap(_.toOption); r <- opt } yield r
于 2014-01-03T04:03:41.977 回答
8

等效代码

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

不是

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

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

在这种情况下没有任何意义。

对于maps 链,您可以使用嵌套for-comprehensions

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

map看起来更好。

于 2014-01-03T01:39:17.577 回答
5

好吧,for理解在它们看起来的方式上具有欺骗性。您的理解扩展为:

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

这显然是错误的,因为您flatMap期望返回类型Future[T]Option[Unit]

虽然有时,为了代码整洁,您可能希望有一个for包含许多此类表达式的循环。在这些情况下,您可以:

 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

以上产生:

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

编辑:虽然如果你使用yield. 如果您不希望将for理解用作表达式,那么您可以根据需要进行操作:

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)))

这种方式不需要隐式。正如编译器foreach在每个连接处使用的那样。-Xprint:typer给出:

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))))))))
  }
于 2014-01-03T01:47:18.930 回答