2

我在 Scala 2.10 中遇到语法问题。

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

4

2 回答 2

7

我最近对此进行了解释——希望有人能找到链接。这是一个反复出现的问题,因此很可能已关闭。

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

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

那么 的表示由f(a, b, c)控制C,that 的表示由 控制B,最终结果的表示由 控制A。因此,对于大多数实际目的,for comprehension 的表示由第一个生成器控制。

那么我所说的“代表”是什么意思?嗯,一个 for 理解通常是一个单子理解(实际上,它只是对像flatMapand这样的方法的一组调用map,所以它可以是任何类型检查的东西)。这意味着给定一个 monadM[A]和一个 function A => M[B],然后您可以转换M[A]M[B]其中Mmonad 是“表示”。

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

但是,存在从Optionto的隐式转换Iterable。在这种情况下,当 Scalab <- a在第一个示例中找到,并且知道它不能通过 anOption因为理解被 a “控制”时List,它会将 theOption转换为 an Iterable,并且一切正常。

但是,在第二种情况下不会发生这种情况。用 an 进行 for 理解是可以的Option,因此无需将其转换为Iterable. 不幸的是,无法将 a 转换List为 an Option(这种转换的结果是什么?),这会导致错误。

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

我衷心建议您查看相关问题并了解 for comprehension 是如何翻译的。

于 2013-02-19T04:56:55.587 回答
2

丹尼尔解释了其中的复杂性。但我想补充一些细节,因为我发现这种行为非常不直观,而且我自己也遇到过这种混音Option问题List。这特别烦人,因为如您所见,它在一个方向上起作用,但在另一个方向上不起作用。

因此,根据您将拥有的理解规则

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自己强加这种转换吗?老实说,我不知道,但我和你一样感到惊讶,它不起作用。

于 2013-02-19T08:59:01.113 回答