Scala中的TraversableOnce,Future和Option用于理解

Ele*_*onk 8 monads scala future

我有一个表示DB记录的字符串ID列表.我想异步地从数据库加载它们,然后异步上传每个记录到远程服务器,然后当完成所有记录上传后,记录上传的记录的ID.

由于我使用的是Scala 2.9.2,我使用的是Twitter的core-util Future实现,但它应该与Monadic转换的2.10期货完全一样.

一般概念是这样的:

def fetch(id: String): Future[Option[Record]]
def upload(record: Record): Future[String]
def notifyUploaded(ids: Seq[String]): Unit

val ids: Seq[String] = ....
Run Code Online (Sandbox Code Playgroud)

我试图通过for comprehension来做到这一点,但fetch返回Future of Option这一事实使得它变得模糊不清并且代码无法编译:

for {
  id <- ids
  maybeRecord <- fetch(id)
  record <- maybeRecord
  uploadedId <- upload(record)
} yield uploadedId
Run Code Online (Sandbox Code Playgroud)

编译此结果会导致以下错误:

scala: type mismatch;
found   : com.twitter.util.Future[String]
required: Option[?]
    uploadedId <- upload(record)
                  ^
Run Code Online (Sandbox Code Playgroud)

我错过了什么?为什么编译器期望uploadedId是一个选项?有什么好办法可以解决这个问题吗?

Imp*_*ive 6

考虑flatMap(或绑定)函数的签名:

trait Monad[M[_]] {
  def flatMap[A](a : M[A], f : A => M[B]) : M[B]
  ....
Run Code Online (Sandbox Code Playgroud)

在你的情况下,你试图使用flatMap一个Option,给它一个f生成一个Future.但正如在上面的签名中,f应该在同一个monad中生成一些它被调用的东西.

Scala在这方面不一定非常有用,因为它非常善于将事物转换Seq为(比如s),这样你就可以得到你可以将任意flatMap调用链接在一起的印象,无论容器如何.

你可能想要的是一个'Monad变换器',它可以让你有一些组合monad的能力.Debasish Ghosh有一篇关于在这里使用Scalaz monad变换器的帖子.