假设我有:
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]]变量的问题的简化,我想在for comprehension中一起使用它们.
理解力真的让它看起来应该都能起作用,不是吗?但是让我们考虑一下你的要求.
首先,请注意for
un-nests:
for {xs <- List(List(5)); x <- xs} yield x
产生
List(5)
现在,我们甚至没有进入类型签名或desugaring,我们可以考虑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
?这比两件事具有相同嵌套的情况更为笼统.好吧,如果T
和U
两者都有一个共同的超类型S
,那么我们就可以查看整个事情S(S(a: A))
,所以我们至少得到一个S
回来.但是在一般情况下呢?
底线是它取决于.例如,让我们考虑一下T=Future
,U=Option
.我们有以下可能性:
Success(Some(a)) Success(None) Failure(t: Throwable)
现在,我们能否提出任何连贯的解缠政策?如果我们打开一个Future
,那么A
你对这个Success(None)
案子有什么用?您没有可用的返回.同样地,如果你试图征服外部Future
,你怎么知道,如果没有明确地以某种方式向编译器陈述它Failure
应该被映射到None
(如果确实它应该 - 也许它应该是默认的!).
所以最重要的是,你不能正确地做到这一点,而不指定每对应该发生什么T[U[_]]
.(我鼓励感兴趣的读者仔细阅读monad和monad变换器的教程.)
但是有一条出路:如果你可以明确地将你U
变成一个T
,或者你的T
变成你的U
,你可以利用展开功能.将a Option
变为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
好吧,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)))))))) }
等效代码
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
s 链,你可以使用嵌套for-comprehensions
for { opt <- res } for { r <- opt } print(!r)
但map
看起来更好.