我有一个Future[Option[String]]
类型的集合,我将它映射到一个返回Future[Option[Profile]]
的函数,但这会创建一个Future[Option[Future[Option[Profile]]]]
的返回类型,因为queryProfile
返回类型是`未来[选项[简介]]'
val users: Future[Option[User]] = someQuery
val email: Future[Option[String]] = users map(opSL => opSL map(_.email) )
val userProfile = email map {opE => opE map {E => queryProfile(E)}}
我需要使用Profile
内部包含的val userProfile
对象而不解压所有这些级别,使用flatMap
或“flatten”的正确方法是什么,或者是否有更好地接近所有人?
答案 0 :(得分:4)
您可以通过以下方式获得“部分未来”:
val maybeProfile: Future[Profile] = users
.collect { case Some(u) => u.email }
.flatMap { email => queryProfile(email) }
.collect { case Some(p) => p }
现在maybeProfile
包含(完全“裸”/未包装)Profile
实例,但前提是它能够找到它。你可以.map
像往常一样用它来做其他事情,这将以通常的方式工作。
如果您想要阻止并等待完成,您将不得不在某个时刻处理丢失的案例。例如:
val optionalProfile: Option[Profile] = Await.result(
maybeProfile
.map { p => Some(p) } // Or just skip the last `collect` above
.recover { case _:NoSuchElementException => None },
1 seconds
)
如果您对Future[Option[Profile]]
感到满意,并且更愿意拥有“展开”魔法,并且处理丢失的案例本地化在一个地方,您可以将上面的两个片段放在一起,如下所示:< / p>
val maybeProfile: Future[Option[Profile]] = users
.collect { case Some(u) => u.email }
.flatMap { email => queryProfile(email) }
.recover { case _:NoSuchElementException => None }
或者像建议的其他答案一样使用Option.fold
:
val maybeProfile: Future[Option[Profile]] = users
.map { _.map(_.email) }
.flatMap { _.fold[Future[Option[Profile]]](Future.successful(None))(queryProfile) }
就个人而言,我发现最后一个选项的可读性较差。
答案 1 :(得分:0)
就我个人而言,我认为像scalaz /猫提供的OptionT这样的monad变换器将是最干净的方法:
val users = OptionT[Future,User](someQuery)
def queryProfile(email:String) : OptionT[Future,Profile] = ...
for {
u <- users
p <- queryProfile(u.email)
} yield p
答案 2 :(得分:0)
我只是创建一个这样的辅助方法:
private def resolveProfile(optEmail: Option[String]): Future[Option[Profile] =
optEmail.fold(Future.successful(None)) { email =>
queryProfile(email).map(Some(_))
}
然后您可以flatMap
原来的email
未来:
val userProfile = email.flatMap(resolveProfile)