Scala用于理解期货和期权

Joh*_*Doe 6 scala playframework

我最近阅读了Manuel Bernhardt的新书Reactive Web Applications.在他的书中,他指出Scala开发人员永远不应该使用.get来检索可选值.

我想提出他的建议,但我正在努力避免.get使用期货的理解.

假设我有以下代码:

for {
        avatarUrl <- avatarService.retrieve(email)
        user <- accountService.save(Account(profiles = List(profile.copy(avatarUrl = avatarUrl)))
        userId <- user.id
        _ <- accountTokenService.save(AccountToken.create(userId, email))
      } yield {
        Logger.info("Foo bar")
      }
Run Code Online (Sandbox Code Playgroud)

通常,我会用AccountToken.create(user.id.get, email)而不是AccountToken.create(userId, email).但是,当试图避免这种不良做法时,我得到以下异常:

[error]  found   : Option[Nothing]
[error]  required: scala.concurrent.Future[?]
[error]         userId <- user.id
[error]                ^
Run Code Online (Sandbox Code Playgroud)

我怎么解决这个问题?

Ori*_*ski 6

第一种选择

如果你真的想要使用for理解,你必须将它分成几个fors,其中每个都使用相同的monad类型:

for {
  avatarUrl <- avatarService.retrieve(email)
  user <- accountService.save(Account(profiles = List(profile.copy(avatarUrl = avatarUrl)))
} yield for {
  userId <- user.id
} yield for {
  _ <- accountTokenService.save(AccountToken.create(userId, email))
}
Run Code Online (Sandbox Code Playgroud)

第二种选择

另一种选择是,以避免Future[Option[T]]完全和使用Future[T]它可以兑现到Failure(e)哪里eNoSuchElementException当你想到一个None(在你的情况下,accountService.save()法):

def saveWithoutOption(account: Account): Future[User] = {
  this.save(account) map { userOpt =>
    userOpt.getOrElse(throw new NoSuchElementException)
  }
}
Run Code Online (Sandbox Code Playgroud)

然后你会有:

(for {
  avatarUrl <- avatarService.retrieve(email)
  user <- accountService.saveWithoutOption(Account(profiles = List(profile.copy(avatarUrl = avatarUrl)))
  _ <- accountTokenService.save(AccountToken.create(user.id, email))
} yield {
  Logger.info("Foo bar")
}) recover {
  case t: NoSuchElementException => Logger.error("boo")
}
Run Code Online (Sandbox Code Playgroud)

第三种选择

回归map/ flatMap并介绍中间结果.