Scala 为什么 flatMap 对待 (x => x) 不同于 (identity)

Scala why flatMap treats (x => x) different than (identity)

首先,map 对待 x => xidentity

相同
List(1,2,3).map(x => x)   //res0: List[Int] = List(1, 2, 3)
List(1,2,3).map(identity) //res1: List[Int] = List(1, 2, 3)

现在让我们将 List[Option[Int]] 转换为 List[Int] 并丢弃所有 None。我们可以通过 .flatten 来做到这一点。但是这个问题的重点是了解 flatMap 如何对待 identity

val maybeNums = List(Some(1), None, Some(-2), None, None, Some(33))

// Works OK, result = List[Int] = List(1, -2, 33)
maybeNums.flatMap(x => x)
maybeNums.flatMap(x => x.map(identity))

// Not working:
maybeNums.flatMap(identity)

Error:(5, 20) type mismatch;
 found   : Option[Int] => Option[Int]
 required: Option[Int] => scala.collection.GenTraversableOnce[?]

问题:为什么 maybeNums.flatMap(identity) 编译出错,而 maybeNums.flatMap(x => x) 正常?

有趣的是我遇到了和你类似的问题。此行为是由于 Option 不是 GenTraversableOnce 但存在到 1 的隐式转换引起的。编译器会告诉你出了什么问题,但不幸的是,由于在 Scala 中经常出现这种情况,它并没有指出错误的真正原因。如果您的集合包含 GenTraversableOnce 类型的元素,flatMap 方法就可以正常工作。

起初我以为隐式转换可以解决这个问题,但事实证明eta扩展需要类型显式匹配。更有趣的是下面的代码可以编译:

ys.flatMap(identity(_))

我假设在这种情况下会发生从 Option[Int] => Option[Int]Option[Int] => GenTraversableOnce[Int] 的隐式转换。

x => x的情况下对x进行了前面提到的隐式转换所以代码可以编译

对 Lampart 的回答进行更详细的解释。

归结为类型推断在 Scala 中的工作方式以及预期类型的​​使用。

要使 maybeNums.flatMap(???) 起作用,??? 必须具有类型 Option[Int] => GenTraversableOnce[?A](其中 ?A 代表某种未知类型)。这是预期的类型。

??? 是类似 x => x 的 lambda 表达式时,参数类型为 Option[Int],正文类型为预期类型 GenTraversableOnce[?A]。主体 没有 预期类型的​​类型是 Option[Int],因此找到并插入从 Option[Int]GenTraversableOnce[Int] 的隐式转换。

???identity 时,它是 identity[?B] 的缩写,表示某些未知类型 ?B(与上面的 ?A 意义相同,但当然,它们不必相同),类型 ?B => ?B 也是如此。所以编译器需要求解一个有两个未知类型的方程:?B => ?B == Option[Int] => GenTraversableOnce[?A]。它匹配参数类型,为 ?B 选择 ?B = Option[Int],但找不到合适的 ?A。打印错误时, ?B 被替换,我在上面写为 ?A 的类型打印为 ? (因为它是唯一剩下的未知数)。

如果 identity(_),则扩展为 x => identity(x)。同样,参数的类型被推断为 Option[Int] 并且主体已计算类型 Option[Int] 和预期类型 GenTraversableOnce[?A].