【问题标题】:Option generator in for comprehension forces other generators to be Option?用于理解的选项生成器强制其他生成器成为选项?
【发布时间】:2016-10-04 12:50:28
【问题描述】:

第一个 for 表达式是基本示例。第二个 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"
//     ^

【问题讨论】:

  • 在这种情况下是的,因为第二个提取器就像 flatMap 所以你需要返回一个选项。为清楚起见,由于隐式转换,您可以在第一种情况下提取“ABC”,这样事物就变成了一个列表并编译,在第二种情况下,“ABC”不会隐式转换为选项。

标签: scala for-comprehension


【解决方案1】:

回答标题问题:是的,第一个生成器为整个 for 表达式“设置了情绪”。请注意,像上面这样的 for 表达式desugared 调用flatMaps 和最后一个map(加上withFilter 用于警卫)。

在您的情况下,第一个 for 表达式被 desugared 转换为以下表达式:

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

这是可行的,因为Predef(在每个 Scala 程序中都隐式导入)提供了从 StringSeq[Char] 的隐式转换。

val implicitlyConverted: Seq[Char] = "ABC"

因此,它会按计划对运行进行类型检查。

现在让我们看看第二个 for 表达式是如何脱糖的:

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[_] 作为结果,而 ("ABC".map(...)) 内的 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 可能会给你一个更笼统的答案。

【讨论】:

  • 答案的学术质量超出预期。非常感谢。
  • @Polymerase 谢谢,很高兴我能帮上忙!
【解决方案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。通常 monads 根本不组合,但在 Scala 中,有些使用 CanBuildFrom

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多