为什么 For Comprehension 生成器会抑制 Option 类型?

Why does For Comprehension generator suppress Option type?

在下面的代码中,版本 1 给出了正确的结果。我对 V2 做了一个小改动。 None 值消失了,这没关系,因为 For Expression 就是这样工作的。但是,V2 中的 yield 输出不再遵循 myList.lift() 返回的数据类型(如 V1 中那样)的原因是什么?

val myList = List(12, 34, "ABC")

版本 1

for { i <- (0 to 3).toList } yield myList.lift(i)
// res1: List[Option[Any]] = List(Some(12), Some(34), Some(ABC), None)

版本 2

for { 
  i <- (0 to 3).toList 
  x <- myList.lift(i)
} yield x
// res2: List[Any] = List(12, 34, ABC)

如果你对 for comprehensions 去糖化

版本 1

List(0, 1, 2, 3).map({ i => 
  myList.lift(i) 
})

版本 2

List(0, 1, 2, 3).flatMap({ i => 
  myList.lift(i).map({ x => x })
})

what is the reason the yield output in V2 no longer respects the data type returned by myList.lift()

yieldList.lift 的输出没有任何影响:

  • myList.lift(i) returns Option[Any]
  • myList.lift(i).map({ x => x }) returns Option[Any]

flatMapOption[Any] 扁平化为 Any(通过丢弃 None 和展开 Some(a: Any) => a

第一种情况脱糖:

// desugar for comprehension:
(0 to 3).toList.map(
  i => myList.lift(i))

第二种情况脱糖:

// desugar for comprehension:
(0 to 3).toList.flatMap(
  i => myList.lift(i).map(
    x => x))

// remove .map(x => x):
(0 to 3).toList.flatMap(
  i => myList.lift(i))

// desugar flatMap:
(0 to 3).toList.map(
  i => myList.lift(i)).flatten

第二种情况简化为第一种情况,末尾有一个.flatten,这解释了结果的差异:res2 = res1.flatten.

到底发生了什么?

Scala 可以将 Option 视为一个序列:

Some(foo) --> Seq(foo)
None      --> Seq()

.flatten只是将序列的序列展平。

如果您对类型感到好奇:

  • scala.collection.Seq.flatten 要求 'inner' 类型隐式转换为 GenTraversableOnce[T]
  • 存在从 Option[T]Iterable[T]
  • 的全局隐式转换
  • Iterable[T] <: GenTraversableOnce[T]

<- 是什么意思?

x <- myList.lift(i) 中的 <- 不会 "t just assign a variable to a value, it " 从“myList.lift(i) 中获取值。当您 "get a value out of" 一个 Option[T] 时,您Some(foo) 得到 fooNone 什么都没有。"Getting nothing" 意味着 yield 没有 "t run at all for a None, so nothing shows up in the result for the " 迭代”当 i = 3.

如果您对为 SeqOption 和 Scala 中的许多其他类型定义的这个 "get a value out of" 概念感到好奇,它是为任何 Monad 定义的。