我在Scala 2.10中遇到for-comprehension的语法问题。
for(a <- List(Some(1,2)); b <- a) yield b
评估为List(1,2)
那么为什么for(a <- Some(List(1,2)); b <- a) yield b
不能评估同样的事情呢?
类型检查器抱怨第二个表达式(b <-a
)说它找到List[Int]
期待Option[?]
答案 0 :(得分:7)
我最近解释过这个问题 - 希望有人可以找到这个链接。这是一个反复出现的问题,所以很可能会被关闭。
无论如何,外部生成器控制表示。这种情况发生在每个级别,所以如果我有这个:
for {
a <- A
b <- B
c <- C
} yield f(a, b, c)
然后f(a, b, c)
的表示由C
控制,其表示由B
控制,最终结果的表示由A
控制。因此,对于大多数实际目的而言,理解的表示由第一个生成器控制。
那么“代表”是什么意思?好吧,理解是一种一元的理解,通常(实际上,它只是对flatMap
和map
等方法的一组调用,所以它可以是任何那样的typechecks) 。这意味着,如果给出了monad M[A]
和函数A => M[B]
,那么您可以在M[A]
中转换M[B]
,其中M
,monad是“表示”
这意味着,在大多数情况下,将Option
和List
组合起来进行理解是不可能的。所有集合在GenTraversableOnce
中都有一个共同的父级,因此组合它们没有问题(尽管事情要比引擎下的内容复杂得多)。
但是,存在从Option
到Iterable
的隐式转换。既然如此,当Scala在第一个示例中找到b <- a
并且知道它无法传递Option
因为理解被List
“控制”时,它会转换为Option
成Iterable
,一切正常。
然而,在第二种情况下不会发生这种情况。使用Option
进行理解是可以的,因此无需将其转换为Iterable
。很遗憾,我们无法将List
转换为Option
(这种转换的结果会是什么?),这会导致错误。
Scala不会“回溯”到a <- Some(List(1, 2))
并对其应用隐式转换,因为Scala中的类型推断只会前进 - 它之前决定的内容将保持不变。
我衷心建议您查看相关问题,并了解如何翻译理解。
答案 1 :(得分:2)
丹尼尔解释了这一点的复杂性。但是我想补充一些细节,因为我发现这种行为非常不直观,而且我自己也遇到过混淆Option
和List
几次的问题。这特别令人讨厌,因为正如你所看到的,它在一个方向上起作用而在另一个方向起作用。
所以根据for-comprehension规则你会有
def test(a: Option[List[Int]]) = a.flatMap(_.map(identity))
失败
<console>:7: error: type mismatch;
found : List[Int]
required: Option[?]
def test(a: Option[List[Int]]) = a.flatMap(_.map(identity))
^
但你可以让它发挥作用:
def test(a: Option[List[Int]]) = (a: Iterable[List[Int]]).flatMap(_.map(identity))
test(Some(List(1,2))) // List(1,2)
或者回到for
:
for(a <- Some(List(1,2)).toIterable; b <- a) yield b
for
应该单独强加这种转换吗?我不是老实说,但我很惊讶它不起作用。