用于理解的 Scala 上的类型不匹配

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4719592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:44:13  来源:igfitidea点击:

Type Mismatch on Scala For Comprehension

scalafor-looptype-mismatchfor-comprehensionscala-option

提问by Felipe Kamakura

Why does this construction cause a Type Mismatch error in Scala?

为什么这种构造会导致 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)

If I switch the Some with the List it compiles fine:

如果我用 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))

This also works fine:

这也很好用:

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

回答by Madoc

For comprehensions are converted into calls to the mapor flatMapmethod. For example this one:

For comprehensions 被转换为对maporflatMap方法的调用。例如这个:

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

becomes that:

变成:

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

Therefore, the first loop value (in this case, List(1)) will receive the flatMapmethod call. Since flatMapon a Listreturns another List, the result of the for comprehension will of course be a List. (This was new to me: For comprehensions don't always result in streams, not even necessarily in Seqs.)

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

Now, take a look at how flatMapis declared in Option:

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

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

Keep this in mind. Let's see how the erroneous for comprehension (the one with Some(1)) gets converted to a sequence of map calls:

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

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

Now, it's easy to see that the parameter of the flatMapcall is something that returns a List, but not an Option, as required.

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

In order to fix the thing, you can do the following:

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

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

That compiles just fine. It is worth noting that Optionis not a subtype of Seq, as is often assumed.

那编译就好了。值得注意的Option是 不是 的子类型Seq,正如通常假设的那样。

回答by huynhjl

An easy tip to remember, for comprehensionswill try to return the type of the collection of the first generator, Option[Int] in this case. So, if you start with Some(1)you should expect a result of Option[T].

一个容易记住的提示,对于推导,在本例中将尝试返回第一个生成器的集合的类型,即 Option[Int]。所以,如果你从Some(1)开始,你应该期待 Option[T] 的结果。

If you want a result of Listtype, you should start with a List generator.

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

Why have this restriction and not assume you'll always want some sort of sequence? You can have a situation where it makes sense to return Option. Maybe you have an Option[Int]that you want to combine with something to get a Option[List[Int]], say with the following function: (i:Int) => if (i > 0) List.range(0, i) else None; you could then write this and get None when things don't "make sense":

为什么有这个限制而不是假设你总是想要某种序列?您可能会遇到需要 return 的情况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

How for comprehensionsare expanded in the general case are in fact a fairly general mechanism to combine an object of type M[T]with a function (T) => M[U]to get an object of type M[U]. In your example, M can be Option or List. In general it has to be the same type M. So you can't combine Option with List. For examples of other things that can be M, look at subclasses of this trait.

如何为内涵在一般情况下被扩展实际上对类型的对象结合一个相当普遍的机制M[T]与功能(T) => M[U]获得类型的对象M[U]。在您的示例中,M 可以是选项或列表。一般来说,它必须是相同的类型M。所以你不能将 Option 与 List 结合使用。有关其他事物的示例M,请查看此 trait 的子类

Why did combining List[T]with (T) => Option[T]work though when you started with the List? In this case the library use a more general type where it makes sense. So you can combine List with Traversable and there is an implicit conversion from Option to Traversable.

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

The bottom line is this: think about what type you want the expression to return and start with that type as the first generator. Wrap it in that type if necessary.

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

回答by sblundy

It probably has something to do with Option not being an Iterable. The implicit Option.option2Iterablewill handle the case where compiler is expecting second to be an Iterable. I expect that the compiler magic is different depending on the type of the loop variable.

这可能与 Option 不是 Iterable 有关。隐式Option.option2Iterable将处理编译器期望第二个是可迭代的情况。我希望编译器的魔法根据循环变量的类型而不同。

回答by user451151

I always found this helpful:

我总是发现这很有帮助:

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)