无需调用flatmap

时间:2015-07-15 15:31:23

标签: scala

是否有更惯用的方法来重复调用map(不使用flatMap)?

请参阅以下示例:

val futureJson : Future[Seq[JsValue]] = futureBlogList.map(
                                            blogList => blogList.map(
                                                    blog => Json.obj("id" -> blog.id, "title" -> blog.title)))

val futureResult : Future[Result] = futureJson.map(jsList => Ok(JsArray(jsList)))

这是Play中的一个函数,在这种情况下需要返回Future [Result]。

我尝试使用"进行理解"但尚未找到 flatMap 的应用程序。我使用flatMap的尝试让我看到了Future [Nothing]。

val futureJson : Future[Nothing] = for {
  blogList : Seq[Blog] <- futureBlogList
  blog : Blog       <- blogList
} yield {
  Json.obj("id" -> blog.id, "title" -> blog.title)
}

2 个答案:

答案 0 :(得分:3)

  • A for comprehension始终是多个flatMaps的组合 和一个map功能。
  • for中箭头右侧的功能 理解,总是具有相同的结果类型(Seq[...]Option[...],...)

您的工作解决方案包含两个map和一个Future以及一个Seq,因此无法将其转换为for comprehension(很好)。

有关理解(以及使用flatMap/map的转换)的更多信息,请参阅Programming in Scala, 1edthis question

如果您真的想要使用for comprehension而不是两个map,我们可以(作为教育练习)将第一个map转换为flatMap。然后,使用blogList的函数应返回Future

futureBlogList.flatMap(blogList =>
  Future.successful(
    blogList.map(blog => Json.obj("id" -> blog.id, "title" -> blog.title))
  )
)

我们可以将第二个map转换为for表达式:

futureBlogList.flatMap(blogList =>
  Future.successful(
    for (blog <- blogList) yield Json.obj("id" -> blog.id, "title" -> blog.title)
  )
)
//.map(identity)

我们可以将其转化为以下(非常难看)以便理解:

for {
  blogList <- futureBlogList
  jsonBlogs <- Future.successful(
    for (blog <- blogList) yield Json.obj("id" -> blog.id, "title" -> blog.title)
  )
) yield jsonBlogs

答案 1 :(得分:1)

for-comprehension 失败的原因是它可以处理相同类型的容器。在您的示例中,您将Future与集合混合。 True ,两者都有map但它们是不同类型的容器,因此它们的map / flatmap链无法互操作,因此无法在换理解。如果您展开 for-comprehension ,您将获得

futurBlogList.flatMap { blogList =>
  blogList.map { blog =>
    Json.obj("id" -> blog.id, "title" -> blog.title)
  }
}

flatMap期望结果为Future[U],但内部的map调用会返回Seq[JSList]

最终结果是,您嵌套两种不同类型的map是表达构造的最惯用方式。最好你可以将这两个调用结合起来:

val futureResult : Future[Result] = futureBlogList.map { blogList =>
  Ok(JsArray(
    blogList.map( blog => Json.obj("id" -> blog.id, "title" -> blog.title))
  ))
}