[英]Chaining futures and options idiomatically
def foo(user, id): Future[Option[Bar]] =
bar(user, id).map(_.map(_.address.flatMap(_.street)))
.flatMap {
case Some(street) =>
baz(user, street).flatMap(_ => get(id))
case None => successful(None)
}
函数栏返回一个Option[UserInfo]
,然后我将其映射到UserInfo
。 Address
也是一个Option
所以我flatMap可以访问street
。 然后,如果有一条街道,我想打电话给baz
,如果没有,那么None
。 忽略业务逻辑,它就是为了这个例子。
这里的代码存在问题,因为它无法编译。
Some(street)
是一个Option
,因为第3行上的flatMap
是在第一个_
而不是_.address
上映射的结果上_.address
。
虽然我可以通过一些括号杂耍等来使用它,但这段代码开始变得难以阅读和推理。
理解是答案吗?
PS:这个例子中可能缺少某些类型信息,所以请问我会详细说明。
编辑:
case class UserInfo(id: UserId, address: Option[Address])
case class Address(street: Option[List[Street]], state: Option[State])
如果我理解方法签名是正确的:
def bar(user, id): Option[UserInfo]
def baz(user, List[Street]): Future[BarId]
def get(id): Option[Bar]
你可以实现这样的方法:
val streetsOpt: Option[List[Street]] = bar(user, id).flatMap(_.flatMap(_.address.flatMap(_.street)))
streetsOpt.flatMap(streets => {
baz(user, streets).map(_ => get(id))
}).getOrElse(successful(None)))
在这一行中快速查看此内容:
baz(user, street).flatMap(_ => get(id))
我不认为最后一个flatMap不会正常工作,因为你似乎传递的函数类似于:
f: => A
即从某个上下文中提取底层值,而flatMap希望您解压缩该值然后换行新的上下文,因此类型为:
f: A => M[B]
当你打电话给
get(id)
不应该将它应用于map方法,而是期望类型的函数:
f: A => B
有两种方法可以处理像这样的嵌套上下文。 我讲了三个我知道的内容:monad变换器(显式,更“标准”,但更冗长),Kleisli(非常优雅,如果你愿意用无点样式编写),或者我的scalaz-transfigure库(有点不成熟,有点不那么明确,但非常简洁)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.