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

时间:2016-08-08 02:38:52

标签: scala for-comprehension

在下面的代码中,版本1给出了正确的结果。我在V2中做了一个小变化。无值已经消失,这是好的,因为这是For Expression的工作方式。但是什么原因导致V2中的yield输出不再尊重myList.lift()返回的数据类型,这是一个Option(如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)

2 个答案:

答案 0 :(得分:6)

贬低第一个案例:

// 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要求'内部'类型隐式转换为GenTraversableOnce[T]
  • Option[T]Iterable[T]
  • 的全局隐式转换
  • Iterable[T] <: GenTraversableOnce[T]

&lt; - 意味着什么?

<-中的x <- myList.lift(i)并不只是为一个值赋值,它“从”myList.lift(i)中获取一个值。当你“获得一个值”时Option[T]foo获得Some(foo)None没有获得任何内容。“一无所获”意味着yield根本无法运行None因此,当i = 3时,“迭代”的结果中没有任何内容显示。

如果您对这个“获取值”的概念感到好奇,该概念是为SeqOption以及Scala中的许多其他类型定义的,那么它就是为任何Monad定义的。

答案 1 :(得分:3)

如果你为了理解而去糖

版本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 })
})
  

V2中的yield输出不再尊重myList.lift()返回的数据类型的原因是什么

yieldList.lift的输出无效:

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

flatMapOption[Any]展平为Any(通过放弃None并取消包裹Some(a: Any) => a