简体   繁体   English

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

[英]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)
    }

Function bar returns an Option[UserInfo] , which I then map to a UserInfo . 函数栏返回一个Option[UserInfo] ,然后我将其映射到UserInfo Address is also an Option so I flatMap that to have access to street . Address也是一个Option所以我flatMap可以访问street Then simply if there exists a street I want to call baz , if not then None . 然后,如果有一条街道,我想打电话给baz ,如果没有,那么None Ignore the business logic, it's made up for the example. 忽略业务逻辑,它就是为了这个例子。

There's a problem with the code here as it won't compile. 这里的代码存在问题,因为它无法编译。

Some(street) is an Option , since the flatMap on line 3 is being called on the result of the mapping on the first _ , instead of _.address . Some(street)是一个Option ,因为第3行上的flatMap是在第一个_而不是_.address上映射的结果上_.address

Whilst I could get this to work with some parenthesis juggling and so on, this code is starting to get hard to read and reason about. 虽然我可以通过一些括号杂耍等来使用它,但这段代码开始变得难以阅读和推理。

Are for-comprehensions the answer? 理解是答案吗?

PS: There might be some type-information missing in this example so please ask and I will elaborate. PS:这个例子中可能缺少某些类型信息,所以请问我会详细说明。

EDIT: 编辑:

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

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

If I understood method signatures right: 如果我理解方法签名是正确的:

 def bar(user, id): Option[UserInfo]
 def baz(user, List[Street]): Future[BarId]
 def get(id): Option[Bar]

You can implement your method something like this: 你可以实现这样的方法:

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

Just quickly looking at this, within this line: 在这一行中快速查看此内容:

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

I don't think that the last flatMap won't work properly because you seem to be passing in a function which is of a type something like: 我不认为最后一个flatMap不会正常工作,因为你似乎传递的函数类似于:

f: => A

ie extracting the underlying value from some context, whereas flatMap expects you to unpack this value and then wrap in a new context, and so has type: 即从某个上下文中提取底层值,而flatMap希望您解压缩该值然后换行新的上下文,因此类型为:

f: A => M[B]

When you are making the call to 当你打电话给

get(id)

Shouldn't this be being applied to a map method instead, which expects a function of type: 不应该将它应用于map方法,而是期望类型的函数:

f: A => B

There are a couple of ways of dealing with nested contexts like this. 有两种方法可以处理像这样的嵌套上下文。 I gave a talk about three that I know: monad transformers (explicit, more "standard", but a bit more verbose), Kleisli (very elegant, if you're willing to write in pointfree style), or my scalaz-transfigure library (a bit immature, a bit less explicit, but very concise). 我讲了三个我知道的内容:monad变换器(显式,更“标准”,但更冗长),Kleisli(非常优雅,如果你愿意用无点样式编写),或者我的scalaz-transfigure库(有点不成熟,有点不那么明确,但非常简洁)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM