Scala中for-comprehension的补救语法问题

时间:2013-02-19 04:25:59

标签: scala list-comprehension

我在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[?]

的时候

2 个答案:

答案 0 :(得分:7)

我最近解释过这个问题 - 希望有人可以找到这个链接。这是一个反复出现的问题,所以很可能会被关闭。

无论如何,外部生成器控制表示。这种情况发生在每个级别,所以如果我有这个:

for { 
  a <- A
  b <- B
  c <- C
} yield f(a, b, c)

然后f(a, b, c)的表示由C控制,其表示由B控制,最终结果的表示由A控制。因此,对于大多数实际目的而言,理解的表示由第一个生成器控制。

那么“代表”是什么意思?好吧,理解是一种一元的理解,通常(实际上,它只是对flatMapmap等方法的一组调用,所以它可以是任何那样的typechecks) 。这意味着,如果给出了monad M[A]和函数A => M[B],那么您可以在M[A]中转换M[B],其中M,monad是“表示”

这意味着,在大多数情况下,将OptionList组合起来进行理解是不可能的。所有集合在GenTraversableOnce中都有一个共同的父级,因此组合它们没有问题(尽管事情要比引擎下的内容复杂得多)。

但是,存在从OptionIterable的隐式转换。既然如此,当Scala在第一个示例中找到b <- a并且知道它无法传递Option因为理解被List“控制”时,它会转换为OptionIterable,一切正常。

然而,在第二种情况下不会发生这种情况。使用Option进行理解是可以的,因此无需将其转换为Iterable。很遗憾,我们无法将List转换为Option(这种转换的结果会是什么?),这会导致错误。

Scala不会“回溯”到a <- Some(List(1, 2))并对其应用隐式转换,因为Scala中的类型推断只会前进 - 它之前决定的内容将保持不变。

我衷心建议您查看相关问题,并了解如何翻译理解。

答案 1 :(得分:2)

丹尼尔解释了这一点的复杂性。但是我想补充一些细节,因为我发现这种行为非常不直观,而且我自己也遇到过混淆OptionList几次的问题。这特别令人讨厌,因为正如你所看到的,它在一个方向上起作用而在另一个方向起作用。

所以根据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应该单独强加这种转换吗?我不是老实说,但我很惊讶它不起作用。