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
.
我从您的问题中推断出getUserDetails
和getSchool
的以下类型:
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]]
.
一旦您拥有Future
的Monad
实例( 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.