“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 是 flatMap
和 map
的组合。带有 <-
的每一行都被转换为 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)
我是一个新的 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 是 flatMap
和 map
的组合。带有 <-
的每一行都被转换为 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)