83

为什么这种构造会在 Scala 中导致类型不匹配错误?

for (first <- Some(1); second <- List(1,2,3)) yield (first,second)

<console>:6: error: type mismatch;
 found   : List[(Int, Int)]
 required: Option[?]
       for (first <- Some(1); second <- List(1,2,3)) yield (first,second)

如果我用 List 切换 Some 它编译得很好:

for (first <- List(1,2,3); second <- Some(1)) yield (first,second)
res41: List[(Int, Int)] = List((1,1), (2,1), (3,1))

这也可以正常工作:

for (first <- Some(1); second <- Some(2)) yield (first,second)
4

5 回答 5

119

For 推导式被转换为对maporflatMap方法的调用。比如这个:

for(x <- List(1) ; y <- List(1,2,3)) yield (x,y)

变成:

List(1).flatMap(x => List(1,2,3).map(y => (x,y)))

因此,第一个循环值(在本例中为List(1))将接收flatMap方法调用。因为flatMapon aList返回 another List,所以 for 理解的结果当然是 a List。(这对我来说是新的:因为理解并不总是导致流,甚至不一定在Seqs 中。)

现在,看看是如何flatMap声明的Option

def flatMap [B] (f: (A) ⇒ Option[B]) : Option[B]

请记住这一点。让我们看看错误的理解(带有 的Some(1))如何转换为一系列 map 调用:

Some(1).flatMap(x => List(1,2,3).map(y => (x, y)))

现在,很容易看出flatMap调用的参数是根据需要返回 aList而非a 的东西Option

为了解决这个问题,您可以执行以下操作:

for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)

这编译得很好。值得注意的是,它不是通常假设Option的 的子类型。Seq

于 2011-01-18T01:38:25.813 回答
32

一个容易记住的提示,因为推导式将尝试返回第一个生成器的集合的类型,在这种情况下是 Option[Int]。所以,如果你从Some(1)开始,你应该期待 Option[T] 的结果。

如果你想要List类型的结果,你应该从 List 生成器开始。

为什么有这个限制,而不是假设你总是想要某种序列?您可能会遇到返回有意义的情况Option。也许你有一个Option[Int]你想与一些东西结合来得到一个Option[List[Int]],比如下面的函数(i:Int) => if (i > 0) List.range(0, i) else None:然后你可以写这个并在事情没有“意义”时得到 None :

val f = (i:Int) => if (i > 0) Some(List.range(0, i)) else None
for (i <- Some(5); j <- f(i)) yield j
// returns: Option[List[Int]] = Some(List(0, 1, 2, 3, 4))
for (i <- None; j <- f(i)) yield j
// returns: Option[List[Int]] = None
for (i <- Some(-3); j <- f(i)) yield j
// returns:  Option[List[Int]] = None

在一般情况下如何扩展for 理解实际上是一种相当通用的机制,可以将类型对象M[T]与函数组合(T) => M[U]以获取类型对象M[U]。在您的示例中,M 可以是选项或列表。一般来说,它必须是相同的类型M。所以你不能把 Option 和 List 结合起来。有关其他可能的示例M,请查看此 trait 的子类

为什么刚开始使用 List 时却要结合工作List[T](T) => Option[T]在这种情况下,库在有意义的地方使用更通用的类型。因此,您可以将 List 与 Traversable 结合使用,并且存在从 Option 到 Traversable 的隐式转换。

底线是:考虑您希望表达式返回什么类型,并将该类型作为第一个生成器开始。如有必要,将其包装在该类型中。

于 2011-01-18T05:23:57.330 回答
4

它可能与 Option 不是 Iterable 有关。隐式Option.option2Iterable将处理编译器期望 second 是 Iterable 的情况。我希望编译器的魔力根据循环变量的类型而有所不同。

于 2011-01-18T01:41:53.073 回答
1

我总是觉得这很有帮助:

scala> val foo: Option[Seq[Int]] = Some(Seq(1, 2, 3, 4, 5))
foo: Option[Seq[Int]] = Some(List(1, 2, 3, 4, 5))

scala> foo.flatten
<console>:13: error: Cannot prove that Seq[Int] <:< Option[B].
   foo.flatten
       ^

scala> val bar: Seq[Seq[Int]] = Seq(Seq(1, 2, 3, 4, 5))
bar: Seq[Seq[Int]] = List(List(1, 2, 3, 4, 5))

scala> bar.flatten
res1: Seq[Int] = List(1, 2, 3, 4, 5)

scala> foo.toSeq.flatten
res2: Seq[Int] = List(1, 2, 3, 4, 5)
于 2018-06-18T22:34:56.427 回答
0

Scala 2.13选项以来IterableOnce

sealed abstract class Option[+A] extends IterableOnce[A] with Product with Serializable

所以下面的理解工作不使用option2Iterable隐式转换

scala> for {
     |   a <- List(1)
     |   b <- Some(41)
     | } yield (a + b)
val res35: List[Int] = List(42)

scala> List(1).flatMap
final override def flatMap[B](f: Int => scala.collection.IterableOnce[B]): List[B]

我们看到的地方List#flatMap需要一个函数IterableOnce。为了理解上面的脱糖,我们得到类似的东西

List(1).flatMap(a => Some(41).map(b => a + b))

这表明没有隐式转换。

但是在 Scala 2.12 及之前Option的版本中不是可遍历/可迭代的实体

sealed abstract class Option[+A] extends Product with Serializable 

所以上面的理解会脱糖到类似的东西

List(1).flatMap(a => option2Iterable(Some(41)).map(b => a + b))(List.canBuildFrom[Int])

我们在哪里看到隐式转换。

它不能以相反的方式工作的原因是理解从哪里开始,Option然后我们尝试链接一个List

scala> for {
     |   a <- Option(1)
     |   b <- List(41)
     | } yield (a + b)
         b <- List(41)
           ^
On line 3: error: type mismatch;
        found   : List[Int]
        required: Option[?]

scala> Option(1).flatMap
final def flatMap[B](f: Int => Option[B]): Option[B]

是因为Option#flatMap接受一个函数Option并将 a 转换ListOption可能没有意义,因为我们会丢失List具有多个元素的 s 的元素。

正如szeiger解释的那样

我认为最近的Option变化实际上使 for comprehensions 用例更容易理解,因为您不再需要隐式转换。Option可以在flatMap任何集合类型的 a 的 RHS 上使用,因为它是IterableOnce(但不是相反,因为Option#flatMaprequires的 RHS Option)。

于 2021-04-10T15:50:33.003 回答