我有这个问题,我每次都要解决这个问题.我无法使用for comprehension来映射Future中包含的内容.
例:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
val f = Future( List("A", "B", "C") )
for {
list <- f
e <- list
} yield (e -> 1)
Run Code Online (Sandbox Code Playgroud)
这给了我错误:
error: type mismatch;
found : List[(String, Int)]
required: scala.concurrent.Future[?]
e <- list
^
Run Code Online (Sandbox Code Playgroud)
但是,如果我这样做,它工作正常:
f.map( _.map( (_ -> 1) ) )
Run Code Online (Sandbox Code Playgroud)
如果我不能通过使用for comprehension来做到这一点,那么它在我的另一个例子中是否适用于我不进行flatmap的原因?我正在使用Scala 2.10.0.
Dan*_*ral 65
好吧,当你有一个单独的多个生成器进行理解时,你会弄平结果类型.也就是说List[List[T]],你得到的不是List[T]:
scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)
scala> for (a <- list) yield for (b <- list) yield (a, b)
res0: List[List[(Int, Int)]] = List(List((1,1), (1,2), (1,3)), List((2,1
), (2,2), (2,3)), List((3,1), (3,2), (3,3)))
scala> for (a <- list; b <- list) yield (a, b)
res1: List[(Int, Int)] = List((1,1), (1,2), (1,3), (2,1), (2,2), (2,3),
(3,1), (3,2), (3,3))
Run Code Online (Sandbox Code Playgroud)
现在,你怎么会压扁Future[List[T]]?它不可能是a Future[T],因为你将获得多个T,而a Future(而不是a List)只能存储其中一个.Option顺便说一下,同样的问题发生在:
scala> for (a <- Some(3); b <- list) yield (a, b)
<console>:9: error: type mismatch;
found : List[(Int, Int)]
required: Option[?]
for (a <- Some(3); b <- list) yield (a, b)
^
Run Code Online (Sandbox Code Playgroud)
最简单的方法是简单地嵌套多个用于理解:
scala> for {
| list <- f
| } yield for {
| e <- list
| } yield (e -> 1)
res3: scala.concurrent.Future[List[(String, Int)]] = scala.concurrent.im
pl.Promise$DefaultPromise@4f498585
Run Code Online (Sandbox Code Playgroud)
回想起来,这种限制应该是非常明显的.问题是几乎所有的例子都使用集合,而且所有集合都是正确的GenTraversableOnce,所以它们可以自由混合.CanBuildFrom除此之外,Scala受到批评的机制使得可以混合使用任意集合并获取特定类型,而不是GenTraversableOnce.
并且,为了使事情变得更加模糊,Option可以将其转换为a Iterable,这样就可以将选项与集合组合在一起,只要该选项不是第一位的.
但在我看来,混淆的主要原因是在教导理解时没有人提到过这种限制.
嗯,我想我明白了.我需要在未来包装,因为for comprehension添加了flatmap.
这有效:
for {
list <- f
e <- Future( list )
} yield (e -> 1)
Run Code Online (Sandbox Code Playgroud)
当我在上面添加时,我还没有看到任何答案.然而,为了扩展这一点,可以在一个理解中进行工作.不确定是否值得继续开销(编辑:通过使用成功应该没有开销).
for {
list1 <- f
list2 <- Future.successful( list1.map( _ -> 1) )
list3 <- Future.successful( list2.filter( _._2 == 1 ) )
} yield list3
Run Code Online (Sandbox Code Playgroud)
附录,半年后.
解决此问题的另一种方法是简单地使用赋值,=而不是使用<-除初始映射返回类型之外的其他类型.
使用赋值时,该行不会进行平面映射.您现在可以自由地执行返回不同类型的显式映射(或其他转换).
如果你有几个转换,其中一个步骤与其他步骤没有相同的返回类型,但你仍然想使用for-comprehension语法,因为它使你的代码更具可读性,这是很有用的.
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
val f = Future( List("A", "B", "C") )
def longRunning( l:List[(String, Int)] ) = Future.successful( l.map(_._2) )
for {
list <- f
e = list.map( _ -> 1 )
s <- longRunning( e )
} yield s
Run Code Online (Sandbox Code Playgroud)
您的原始版本并不因为编译List和Future不同的单子.要了解为什么这是一个问题,请考虑它的荒谬之处:
f.flatMap(list => list.map(e => e -> 1))
Run Code Online (Sandbox Code Playgroud)
显然list.map(_ -> 1)是(String, Int)一对对的列表,因此我们的参数flatMap是一个函数,它将字符串列表映射到这些对的列表.但我们需要将字符串列表映射到Future某种类型的东西.所以这不编译.
你的答案中的版本会编译,但它不会做你想要的.它不知道这个:
f.flatMap(list => Future(list).map(e => e -> 1))
Run Code Online (Sandbox Code Playgroud)
这次排序的类型,但我们没有做任何有趣的事情 - 我们只是将价值从中拉出来Future,将其重新放入Future,并映射结果.所以Future[(List[String], Int)]当我们想要的时候,我们最终得到了类型的东西Future[List[(String, Int)]].
你正在做的是一种带有两个(不同的)嵌套monad的双映射操作,这不是一个for理解能帮助你的东西.幸运的f.map(_.map(_ -> 1))是,你想做的就是你想要的,而且简洁明了.