Scala理解中的Future [Option] [英] Future[Option] in Scala for-comprehensions

查看:167
本文介绍了Scala理解中的Future [Option]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个函数返回期货.我正在尝试使用收益理解将第一个函数的修改结果反馈到另一个函数中.

I have two functions which return Futures. I'm trying to feed a modified result from first function into the other using a for-yield comprehension.

此方法有效:

  val schoolFuture = for {
    ud <- userStore.getUserDetails(user.userId)
    sid = ud.right.toOption.flatMap(_.schoolId)
    s <- schoolStore.getSchool(sid.get) if sid.isDefined
  } yield s

但是我对在那里有一个"if"不满意,看来我应该可以使用地图了.

However I'm not happy with having the "if" in there, it seems that I should be able to use a map instead.

但是当我尝试使用地图时:

But when I try with a map:

  val schoolFuture: Future[Option[School]] = for {
    ud <- userStore.getUserDetails(user.userId)
    sid = ud.right.toOption.flatMap(_.schoolId)
    s <- sid.map(schoolStore.getSchool(_))
  } yield s

我收到一个编译错误:

[error]  found   : Option[scala.concurrent.Future[Option[School]]]
[error]  required: scala.concurrent.Future[Option[School]]
[error]         s <- sid.map(schoolStore.getSchool(_))

我尝试了一些变化,但是还没有发现任何有吸引力的可行方法.谁能建议一个更好的理解和/或解释我的第二个示例出了什么问题?

I've played around with a few variations, but haven't found anything attractive that works. Can anyone suggest a nicer comprehension and/or explain what's wrong with my 2nd example?

这是Scala 2.10的一个最小但完整的可运行示例:

Here is a minimal but complete runnable example with Scala 2.10:

import concurrent.{Future, Promise}

case class User(userId: Int)
case class UserDetails(userId: Int, schoolId: Option[Int])
case class School(schoolId: Int, name: String)

trait Error

class UserStore {
  def getUserDetails(userId: Int): Future[Either[Error, UserDetails]] = Promise.successful(Right(UserDetails(1, Some(1)))).future
}

class SchoolStore {
  def getSchool(schoolId: Int): Future[Option[School]] = Promise.successful(Option(School(1, "Big School"))).future
}

object Demo {
  import concurrent.ExecutionContext.Implicits.global

  val userStore = new UserStore
  val schoolStore = new SchoolStore

  val user = User(1)

  val schoolFuture: Future[Option[School]] = for {
    ud <- userStore.getUserDetails(user.userId)
    sid = ud.right.toOption.flatMap(_.schoolId)
    s <- sid.map(schoolStore.getSchool(_))
  } yield s
}

推荐答案

This answer to a similar question about Promise[Option[A]] might help. Just substitute Future for Promise.

我从您的问题中推断出getUserDetailsgetSchool的以下类型:

I'm inferring the following types for getUserDetails and getSchool from your question:

getUserDetails: UserID => Future[Either[??, UserDetails]]
getSchool: SchoolID => Future[Option[School]]

由于您忽略了Either中的失败值,而是将其转换为Option,因此实际上有两个类型为A => Future[Option[B]]的值.

Since you ignore the failure value from the Either, transforming it to an Option instead, you effectively have two values of type A => Future[Option[B]].

一旦您拥有FutureMonad实例( scalaz ,或者您也可以按照我所链接的答案编写自己的答案),将OptionT转换器应用于您的问题看起来像这样:

Once you've got a Monad instance for Future (there may be one in scalaz, or you could write your own as in the answer I linked), applying the OptionT transformer to your problem would look something like this:

for {
  ud  <- optionT(getUserDetails(user.userID) map (_.right.toOption))
  sid <- optionT(Future.successful(ud.schoolID))
  s   <- optionT(getSchool(sid))
} yield s

请注意,为了保持类型的兼容性,ud.schoolID被包装在(已经完成的)Future中.

Note that, to keep the types compatible, ud.schoolID is wrapped in an (already completed) Future.

这种理解的结果将是类型OptionT[Future, SchoolID].您可以使用转换器的run方法提取类型为Future[Option[SchoolID]]的值.

The result of this for-comprehension would have type OptionT[Future, SchoolID]. You can extract a value of type Future[Option[SchoolID]] with the transformer's run method.

这篇关于Scala理解中的Future [Option]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆