用于理解的选项生成器强制其他生成器为选项?

时间:2016-10-04 12:50:29

标签: scala for-comprehension

表达式的第一个是基本示例。第二个for-expr引入了一个轻微的变化,我认为会产生相同的输出。但由于编译错误而失败。是什么原因以及如何解决?

for {
    n <- List(1,2)
    c <- "ABC"
} yield s"$c$n"
//res0: List[String] = List(A1, B1, C1, A2, B2, C2)

for {
    opt <- List(None, Some(1), None, None, Some(2), None)
    n <- opt
    c <- "ABC"
} yield s"$c$n"
//Error:(14, 5) type mismatch;
//found   : scala.collection.immutable.IndexedSeq[String]
//required: Option[?]
//  c <- "ABC"
//     ^

2 个答案:

答案 0 :(得分:2)

“修复”它的另一种方法是更改​​顺序:

for {
    opt <- List(None, Some(1), None, None, Some(2), None)
    c <- "ABC"
    n <- opt
} yield s"$c$n"
//> List[String] = List(A1, B1, C1, A2, B2, C2)

这很有效,因为Scala可以将Option转换为List,但不能转向相反的方向。

事实上,它比简单的转换要复杂得多,涉及CanBuildFrom。通常monad根本不构成,但在Scala中,有些使用CanBuildFrom

答案 1 :(得分:1)

回复标题问题:是的,第一个生成器&#34;设置心情&#34;为了整个表达。请注意,上面提到的for-expression是 desugared flatMap的调用和最后的map(加上withFilter的守卫)。

在你的情况下,第一个for-expression是 desugared 到下面的表达式中:

List(1, 2).flatMap(n => "ABC".map(c => s"$c$n"))

这可以起作用,因为Predef(在每个Scala程序中隐式导入)提供了StringSeq[Char]的隐式转换。

val implicitlyConverted: Seq[Char] = "ABC"

因此,它按类型检查运行。

现在让我们来看看第二个for-expression是如何 desugared

List(None, Some(1), None, None, Some(2), None).flatMap(opt => opt.flatMap(n => "ABC".map(c => s"$c$n")))

同样,我们有与上面相同的类型错误,如果我们将表达式分成几行,我们可能会看到问题更好一点:

List(None, Some(1), None, None, Some(2), None).flatMap {
  opt =>
    opt.flatMap {
      n =>
        "ABC".map {
          c =>
            s"$c$n"
        }
    }
}

这给我们带来以下错误:

<console>:12: error: type mismatch;
 found   : scala.collection.immutable.IndexedSeq[String]
 required: Option[?]
                      "ABC".map {
                                ^

第二个flatMap预计会产生Option[_],而map内的"ABC".map(...)会返回IndexedSeq[String]

所以,这就是原因。我们如何解决这个问题?最简单的解决方案可能涉及使用警卫并强制提取Option中的值,如下所示:

for {
  n <- List(None, Some(1), None, None, Some(2), None)
  c <- "ABC" if n.isDefined
} yield s"$c${n.get}"

解决这一特定问题的一般方法涉及monad变换器,因为问题的根源在于monad不构成;问题非常广泛和复杂,或许this reply可能会给你一个更一般的答案。