我喜欢scala的类型安全性,但我一直遇到的一个运行时错误是
Future.filter predicate is not satisfied
我明白我为什么会犯这个错误,只是想寻求如何最好地解决这个错误并优雅地处理它的建议,或者我做错了?
val r: Future[play.api.mvc.Result] = for {
account <- accountServer.get(...)
if account.isConfirmed
orders <- orderService.get(account, ...)
} yield {
...
}
如果帐户没有得到确认,我将得到上述错误。
我本以为,由于过滤器有可能失败,scala会使收益率返回值成为Option。不
filter
对Future
没有意义,因为类型系统不知道else
情况下返回什么,所以依赖它(通过使用if保护)是不安全的。但你可以在一个内这样做,以便理解,以实现相同的事情:
val r: Future[play.api.mvc.Result] = for {
account <- accountServer.get(...)
orders <- if (account.isConfirmed) orderService.get(account, ...)
else Future.successful(Seq.empty)
} yield {
...
}
(正如Jean-Logeart的回答,但在理解范围内)
您可能想要使用一个简单的flatMap
,在其中您可以指定一个else
的情况:
val orders = accountServer.get(...)
.flatMap { account =>
if(account.isConfirmed) orderService.get(account, ...)
else Future.successful(Seq.empty)
}
用于理解的A只是foreach
、map
、flatMap
、filter
等链的语法糖。当您编写if account.isConfirmed
时,这相当于使用Future的filter
方法。
已解析的Future的值包含在Try中,Try可以采取成功或失败的形式。(将其与Option进行比较,后者可以采取Some或None的形式。)
就像Option的filter
在包含的值使谓词失败时将Some转换为None一样,Future/Try filter
方法将Success转换为Failure。如果您查看Future.filter或Try.filter的实现,这是通过返回一个Failure来完成的,其中包含您提到的predicate is not satisfied
消息的new NoSuchElementException
。
因此,错误消息本身完全是经过设计的,并且与类型系统一致。理解过程中的任何一步都只会投射出另一个未来,所以写if account.isConfirmed
要么通过筛选,要么失败。
这就是为什么Scala在使用if
/filter
时不会将Future[Result]更改为Future[Option[Result]]的原因。如果你想产生一个成功的包含Future的Option或一个空的Seq,你必须明确地这样做(正如Jean Logeart和Alvaro Carrasco的回答所示)。