flatMap 应用于 List[Option[T]] 时的行为

Behavior of flatMap when applied to List[Option[T]]

让我们看一下这段代码:

scala> val a = List(Some(4), None)
a: List[Option[Int]] = List(Some(4), None)
scala> a.flatMap( e=> e)
List[Int] = List(4)

为什么要在 List[Option[T]] returns 上应用带有函数 { e => e }flatMap 和删除 None 元素的 List[T]

具体来说,其背后的概念推理是什么——它是否基于函数式编程中的某些现有理论?这种行为在其他函数式语言中是否常见?

这虽然确实有用,但同时也让人觉得有点神奇和随意。

编辑:

感谢您的反馈和回答。 我重写了我的问题,以更加强调问题的概念性。比起 Scala 特定的实现细节,我更感兴趣的是了解它背后的正式概念。

我假设你的意思是同时支持映射和过滤 flatMap:

scala> List(1, 2).flatMap {
     |   case i if i % 2 == 0 => Some(i)
     |   case i => None
     | }
res0: List[Int] = List(2)

之所以有效,是因为 Optioncompanion object 包含从 Option[A]Iterable[A] 的隐式转换,即 GenTraversableOnce[A],这就是 flatMap 期望作为其参数函数的 return 类型。

这是一个方便的习惯用法,但它在其他函数式语言(至少我熟悉的那些)中并不存在,因为它依赖于 Scala 的子类型、隐式转换等奇怪的组合。Haskell 例如通过 mapMaybe for lists 提供了类似的功能。

我们先来看一下Scaladoc for Option的伴生对象。在那里我们看到一个隐式转换:

implicit def option2Iterable[A](xo: Option[A]): Iterable[A]

这意味着任何选项都可以隐式转换为 Iterable,从而产生具有零个或一个元素的集合。如果你有一个 Option[A] 而你需要一个 Iterable[A],编译器会为你添加转换。

在你的例子中:

val a = List(Some(4), None)
a.flatMap(e => e)

我们正在调用 List.flatMap,它接受一个函数 A => GenTraversableOnce[B]。在这种情况下,AOption[Int]B 将被推断为 Int,因为通过隐式转换的魔力, e 在该函数中返回时将从 Option[Int] 转换为 Iterable[Int]GenTraversableOnce 的子类型)。

至此,我们基本上完成了以下工作:

List(List(1), Nil).flatMap(e => e)

或者,使我们的隐式显式:

List(Option(1), None).flatMap(e => e.toList)

flatMap 然后在 Option 上工作,就像它在 Scala 中对任何线性集合所做的那样:采用 A => List[B] 的函数(再次简化)并生成 List[B] 的扁平集合,取消嵌套过程中的嵌套集合。

对您的问题的简短回答是:List 类型的 flatMap 方法被定义为使用更通用的函数类型,而不仅仅是只产生 [=13] 的函数=] 结果类型。

一般结果类型为IterableOnce[B],如faltMap方法签名所示:final def flatMap[B](f: (A) => IterableOnce[B]): List[B]flatMap implementation 相当简单,因为它将 f 函数应用于每个元素并在嵌套的 while 循环中迭代结果。嵌套循环的所有结果都添加到 List[B].

类型的结果中

因此,flatMap 适用于任何从每个列表元素生成 IterableOnce[B] 结果的函数。 IterableOnce 是一个特征,它定义了一个最小接口,它被所有可迭代的 classes 继承,包括所有集合类型(SetMap 等)和 Option class。

Option class implementation returns collection.Iterator.emptyNonecollection.Iterator.single(x)Some(x)。因此 flatMap 方法会跳过 None 元素。

题目使用了identity函数。当目的是扁平化 iterable 元素时,最好使用 flatten 方法。

scala> val a = List(Some(4), None)
a: List[Option[Int]] = List(Some(4), None)

scala> a.flatten
res0: List[Int] = List(4)