假设我有:
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 comprehension中使用Future [Option [Boolean]]来提取或转换布尔值?
注意:这是我目前使用许多Future [Option [Boolean]]变量的问题的简化,我想在一起使用一起理解。
答案 0 :(得分:12)
for-comprehension真的让它看起来应该都可行,不是吗?但是让我们考虑一下你的要求。
首先,请注意for
取消嵌套:
for {xs <- List(List(5)); x <- xs} yield x
产生
List(5)
现在,我们甚至没有进入类型签名或贬低,我们可以考虑将List
替换为任意类型T
,我们将调用包含的类型A
:< / p>
for { xs <- T(T(a: A)); x <- xs } yield x
我们应该得到一个
T[A]
回来(可能是我们投入的那个,但是这些类型实际上并没有向我们承诺)。
好的,但是呢?
for { xs <- T(U(a: A)); x <- xs } yield x
?这比两件事具有相同嵌套的情况更为笼统。好吧,如果T
和U
都有一个共同的超类型S
,那么我们只能将整个内容视为S(S(a: A))
,因此我们至少会获得S
T=Future
1}}回来。但是在一般情况下呢?
底线是它取决于。例如,让我们考虑U=Option
,Success(Some(a))
Success(None)
Failure(t: Throwable)
的情况。我们有以下可能性:
Future
现在,我们能否提出任何一致的解缠政策?如果我们打开A
,那么Success(None)
用于Future
的情况是什么?您没有可用的返回。同样,如果你试图征服外部Failure
,你怎么知道,如果没有明确地向编译器陈述它,None
应该映射到T[U[_]]
(如果它确实应该 - - 它应该是默认的!)。
所以最重要的是,你只是不能正确地执行此操作而不指定每对U
应该发生什么。 (我鼓励感兴趣的读者仔细阅读monads和monad transformers上的教程。)
但还有一条出路:如果您可以明确地将T
变为T
,或将U
明确变为Option
,则可以利用展开能力。将Future
转换为for { opt <- res; r <- Future(opt.get) } yield r
非常容易,因此最简单的解决方案是
none.get
(只允许在Future
上抛出异常)。或者,您可以将Option
转换为for { opt <- res.value.flatMap(_.toOption); r <- opt } yield r
稍微丑陋的
{{1}}
答案 1 :(得分: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)))
在这种情况下没有任何意义。
对于map
的链,您可以使用嵌套的for-comprehensions
for { opt <- res }
for { r <- opt }
print(!r)
但map
看起来更好。
答案 2 :(得分: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))))))))
}