在 for 理解中将选项与列表组合会导致类型不匹配,具体取决于顺序
为什么这种构造会导致 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)
Why does this construction cause a Type Mismatch error in 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:
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)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
For 推导式会转换为对
map
或flatMap
方法的调用。例如,这个:变成那个:
因此,第一个循环值(在本例中为
List(1)
)将接收flatMap
方法调用。由于List
上的flatMap
返回另一个List
,因此 for 理解的结果当然是一个List
。 (这对我来说是新的:因为理解并不总是产生流,甚至不一定是Seq
。)现在,看看
flatMap
是如何声明的选项
:记住这一点。让我们看看理解错误(带有
Some(1)
的错误)如何转换为一系列映射调用:现在,很容易看出
flatMap
的参数> 调用会返回一个List
,但不会根据需要返回Option
。为了解决这个问题,您可以执行以下操作:
编译得很好。值得注意的是,
Option
并不是通常假设的Seq
的子类型。For comprehensions are converted into calls to the
map
orflatMap
method. For example this one:becomes that:
Therefore, the first loop value (in this case,
List(1)
) will receive theflatMap
method call. SinceflatMap
on aList
returns anotherList
, the result of the for comprehension will of course be aList
. (This was new to me: For comprehensions don't always result in streams, not even necessarily inSeq
s.)Now, take a look at how
flatMap
is declared inOption
: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:Now, it's easy to see that the parameter of the
flatMap
call is something that returns aList
, but not anOption
, as required.In order to fix the thing, you can do the following:
That compiles just fine. It is worth noting that
Option
is not a subtype ofSeq
, as is often assumed.一个容易记住的提示是,理解将尝试返回第一个生成器的集合类型,在本例中为 Option[Int]。因此,如果您从 Some(1) 开始,您应该期待 Option[T] 的结果。
如果您想要列表类型的结果,您应该从列表生成器开始。
为什么有这个限制而不假设你总是想要某种序列?您可能会遇到返回
Option
有意义的情况。也许您有一个Option[Int]
,您想将其与某些东西组合以获得Option[List[Int]]
,例如使用以下函数:( i:Int) => if (i > 0) List.range(0, i) else None
;然后,您可以编写此代码,并在事情不“有意义”时得到 None:在一般情况下如何扩展理解实际上是组合
类型的对象的相当通用的机制M[T]
具有函数(T) =>; M[U]
获取M[U]
类型的对象。在您的示例中,M 可以是选项或列表。一般来说,它必须是相同的类型M
。所以你不能将 Option 与 List 结合起来。有关可以是M
的其他内容的示例,请参阅 此特征的子类。为什么将
List[T]
与(T) => 结合起来?当您开始使用列表时,Option[T]
可以工作吗?在这种情况下,库在有意义的情况下使用更通用的类型。因此,您可以将 List 与 Traversable 结合起来,并且存在从 Option 到 Traversable 的隐式转换。底线是这样的:考虑一下您希望表达式返回什么类型,并以该类型作为第一个生成器开始。如有必要,将其包裹在该类型中。
An easy tip to remember, for comprehensions will 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].
If you want a result of List type, you should start with a List generator.
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 anOption[Int]
that you want to combine with something to get aOption[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":How for comprehensions are 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 typeM[U]
. In your example, M can be Option or List. In general it has to be the same typeM
. So you can't combine Option with List. For examples of other things that can beM
, look at subclasses of this 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.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.
这可能与 Option 不是 Iterable 有关。隐式
Option.option2Iterable
将处理编译器期望第二个是 Iterable 的情况。我预计编译器的魔力会根据循环变量的类型而有所不同。It probably has something to do with Option not being an Iterable. The implicit
Option.option2Iterable
will 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.我总是发现这很有帮助:
I always found this helpful:
由于 Scala 2.13 选项被设为
IterableOnce
,因此以下内容理解在不使用
option2Iterable
隐式转换的情况下工作,我们看到
List#flatMap
将一个函数转换为IterableOnce
。为了理解上面的脱糖,我们得到了类似的结果,表明没有隐式转换。
然而,在 Scala 2.12 及之前的版本中,
Option
不是可遍历/可迭代的实体,因此上面的理解将脱糖为类似于
我们看到隐式转换的内容。
它不能以相反的方式工作,因为理解从
Option
开始,然后我们尝试链接List
是因为
Option#flatMap
使用Option
函数并将List
转换为Option
可能没有意义,因为我们会丢失List
的元素> 具有多个元素。正如 szeiger 解释的那样
Since Scala 2.13 Option was made
IterableOnce
so the following for comprehension works without the use of
option2Iterable
implicit conversionwhere we see
List#flatMap
takes a function toIterableOnce
. Desugaring above for comprehension we get something likewhich show the absence of the implicit conversion.
However in Scala 2.12 and before
Option
was not a traversable/iterable entityso the above for comprehension would desugar to something like
where we see the implicit conversion.
The reason it does not work the other way around where for comprehension begins with
Option
and then we try to chain aList
is because
Option#flatMap
takes a function toOption
and converting aList
toOption
probably does not make sense because we would lose elements forList
s with more than one element.As szeiger explains