Sim*_*n C 8 scala for-comprehension
假设我有:
val res:Future[Option[Boolean]] = Future(Some(true))
Run Code Online (Sandbox Code Playgroud)
我能做到:
res.map(opt => opt.map(r => print(!r)))
Run Code Online (Sandbox Code Playgroud)
我想对此的理解是:
for {
opt <- res
r <- opt
} yield (print(!r))
Run Code Online (Sandbox Code Playgroud)
但这不起作用!我收到一个错误,即:
error: type mismatch;
found : Option[Unit]
required: scala.concurrent.Future[?]
r <- opt
Run Code Online (Sandbox Code Playgroud)
如何在for comprehension中使用Future [Option [Boolean]]来提取或转换布尔值?
注意:这是我目前使用许多Future [Option [Boolean]]变量的问题的简化,我想在for comprehension中一起使用它们.
Rex*_*err 12
理解力真的让它看起来应该都能起作用,不是吗?但是让我们考虑一下你的要求.
首先,请注意forun-nests:
for {xs <- List(List(5)); x <- xs} yield x
Run Code Online (Sandbox Code Playgroud)
产生
List(5)
Run Code Online (Sandbox Code Playgroud)
现在,我们甚至没有进入类型签名或desugaring,我们可以考虑List用一些任意类型替换T,我们将调用包含的类型A:
for { xs <- T(T(a: A)); x <- xs } yield x
Run Code Online (Sandbox Code Playgroud)
我们应该得到一个
T[A]
Run Code Online (Sandbox Code Playgroud)
回来(可能是我们投入的那个,但实际上并没有向我们承诺).
好的,但是呢
for { xs <- T(U(a: A)); x <- xs } yield x
Run Code Online (Sandbox Code Playgroud)
?这比两件事具有相同嵌套的情况更为笼统.好吧,如果T和U两者都有一个共同的超类型S,那么我们就可以查看整个事情S(S(a: A)),所以我们至少得到一个S回来.但是在一般情况下呢?
底线是它取决于.例如,让我们考虑一下T=Future,U=Option.我们有以下可能性:
Success(Some(a))
Success(None)
Failure(t: Throwable)
Run Code Online (Sandbox Code Playgroud)
现在,我们能否提出任何连贯的解缠政策?如果我们打开一个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
Run Code Online (Sandbox Code Playgroud)
(只是让异常被抛出none.get).或者,你可以把它Future变成一个Option略带丑陋的东西
for { opt <- res.value.flatMap(_.toOption); r <- opt } yield r
Run Code Online (Sandbox Code Playgroud)
等效代码
for {
opt <- res
r <- opt
} yield (print(!r))
Run Code Online (Sandbox Code Playgroud)
不是
res.map(opt => opt.map(r => print(!r)))
Run Code Online (Sandbox Code Playgroud)
但
res.flatMap(opt => opt.map(r => print(!r)))
Run Code Online (Sandbox Code Playgroud)
在这种情况下它没有任何意义.
对于maps 链,你可以使用嵌套for-comprehensions
for { opt <- res }
for { r <- opt }
print(!r)
Run Code Online (Sandbox Code Playgroud)
但map看起来更好.
好吧,for理解是在欺骗他们的样子.你的理解扩展到:
res.flatMap(opt => opt.map(r => print(!r))
Run Code Online (Sandbox Code Playgroud)
这显然是错误的,因为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
Run Code Online (Sandbox Code Playgroud)
以上产生:
res.flatMap[Option[Unit]](((opt: Option[Boolean]) =>
toFuture[Option[Unit]](opt.map[Unit](((r: Boolean) => print(!r))))))
Run Code Online (Sandbox Code Playgroud)
编辑:如果您正在使用,您需要承担所有的痛苦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)))
Run Code Online (Sandbox Code Playgroud)
这样就不需要隐式了.编译器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))))))))
}
Run Code Online (Sandbox Code Playgroud)