“required: scala.collection.GenTraversableOnce[?]” 这个理解错误

“required: scala.collection.GenTraversableOnce[?]” error from this for-comprehension

我是一个新的 Scala 开发者,有点被类型问题困扰。有时我仍然会因为处理期货而被绊倒,我认为这就是其中之一。这一段代码…

// do some stuff with a collection of List[Future[ClientArticle]]
Future.sequence(listFutureClonedArticles).map( clonedArticles =>
    for {
        // create persistent records of the cloned client articles, and discard the response
        _ <- clonedArticles.map(clonedArticle => clientArticleDAO.create(clonedArticle))

        // add cloned articles to a batch of articles, and discard the response
        _ <- batchDAO.addArticlesToExistingBatch(destinationBatch._id, clonedArticles.map(_._id))

    } yield {

        // ultimately just return the cloned articles
        clonedArticles
    }
)

… 正在产生此编译器错误:

[error] /.../app/services/BatchServiceAPI.scala:442: type mismatch;
[error]  found   : scala.concurrent.Future[List[model.ClientArticle]]
[error]  required: scala.collection.GenTraversableOnce[?]
[error]                             _ <- batchDAO.addArticlesToExistingBatch(destinationBatch._id, clonedArticles.map(_._id))
[error]                               ^

addArticlesToExistingBatch() 的参数似乎是方法签名的正确类型:

/** Adds a list of id's to a batch by it's database ID. */
def addArticlesToExistingBatch(batchId: ID, articleIds: List[ID])(implicit ec: ExecutionContext): Future[Return]

当然,我可能也误解了 for comprehension 的工作原理。我不明白如何在 <- 运算符处发生错误,how/why 那时也不会有类型期望。

谁能帮我理解这里需要做什么?

=== 21分钟后... ===

有意思。当我停止使用 for comprehension 并将它们分成两个单独的映射时,它会编译。

// create cloned client articles
Future.sequence(listFutureClonedArticles).map(clonedArticles =>
    clonedArticles.map(clonedArticle => clientArticleDAO.create(clonedArticle)))

// add cloned articles to destination batch
Future.sequence(listFutureClonedArticles).map(clonedArticles =>
    batchDAO.addArticlesToExistingBatch(destinationBatch._id, clonedArticles.map(_._id)))

是的,我想我还是不太明白 for-comprehensions。我认为它们可以用来汇总多个 Future 操作。为什么这在这种情况下不起作用

for comprehension 是 flatMapmap 的组合。带有 <- 的每一行都被转换为 flatMap 但最后一行被转换为 map.

所以,你编码

  for {
    // create persistent records of the cloned client articles, and discard the response
    _ <- clonedArticles.map(clonedArticle => clientArticleDAO.create(clonedArticle))
    // add cloned articles to a batch of articles, and discard the response
    _ <- batchDAO.addArticlesToExistingBatch(destinationBatch._id, clonedArticles.map(_._id))
  } yield {
    // ultimately just return the cloned articles
    clonedArticles
  }

转换为

  clonedArticles.map(clonedArticle => clientArticleDAO.create(clonedArticle)).flatMap { _ =>
    batchDAO.addArticlesToExistingBatch(destinationBatch._id, clonedArticles.map(_._id)).map { _ =>
      clonedArticles
    }
  }

因为 clonedArticles 是一个 List 并且列表的 flatMap 的签名是

final override def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That = ??? 

如果您查看 flatMap 所需的参数,它需要一个函数 A => GenTraversableOnce 但在您的函数中您传递的是一个函数 A => Future,这就是问题所在。

我已经尝试用简单的函数来模拟你的问题,你可以试试:

  import scala.concurrent._
  import scala.concurrent.ExecutionContext.Implicits.global
  val listOfFuture: List[Future[Int]] = (1 to 10).map(Future(_)).toList
  def f(i: List[Int]): Future[String] = Future(s"very complex logic: ${i.sum}")
  def create(i: Int): Future[Unit] = Future(println(s"creating something complex: $i"))

 Future.traverse(listOfFuture){ futureX =>
   for {
     x <- futureX
     _ <- create(x)
   } yield x
  }.flatMap(f)