用惯用的方式链接期货和期权

时间:2014-12-15 08:08:43

标签: scala coding-style future optional

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],然后我将其映射到UserInfoAddress也是Option,所以我使用flatMap来访问street。然后,如果存在我想要呼叫baz的街道,如果没有,则None。忽略业务逻辑,它就是为了这个例子。

这里的代码存在问题,因为它无法编译。

Some(street)Option,因为第3行上的flatMap是针对第一个_上的映射结果调用的,而不是_.address }。

虽然我可以通过一些括号杂耍等来解决这个问题,但是这段代码开始变得难以阅读和推理。

for-comprehension是答案吗?

P.S:在这个例子中可能会遗漏一些类型信息,所以请问,我会详细说明。

编辑:

case class UserInfo(id: UserId, address: Option[Address])

case class Address(street: Option[List[Street]], state: Option[State])

3 个答案:

答案 0 :(得分:3)

如果我理解方法签名:

 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)))

答案 1 :(得分:0)

快速查看此内容:

baz(user, street).flatMap(_ => get(id))

我认为最后一个flatMap不会正常工作,因为你似乎传递的函数类似于:

f: => A

即。从某个上下文中提取底层值,而flatMap希望你解压缩这个值然后换行一个新的上下文,所以有类型:

f: A => M[B]

当您拨打

get(id)

不应该将它应用于map方法,而是需要类型为

的函数
f: A => B

答案 2 :(得分:0)

有两种方法可以处理像这样的嵌套上下文。 I gave a talk我知道的大约三个:monad变形金刚(明确,更多"标准",但更冗长),Kleisli(非常优雅,如果你愿意以无点的方式写作),或我的scalaz-transfigure库(有点不成熟,有点不那么明确,但非常简洁)。