用于理解的选项生成器强制其他生成器成为选项?
Option generator in for comprehension forces other generators to be Option?
第一个 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"
// ^
回复题主问题:对,第一个生成器"sets the mood"对整个for-expression。请注意,像上面那样的 for-expressions desugared 调用 flatMap
s 和最后的 map
(加上 withFilter
用于守卫).
在您的例子中,第一个 for-expression 脱糖 为以下表达式:
List(1, 2).flatMap(n => "ABC".map(c => s"$c$n"))
这是可行的,因为 Predef
(在每个 Scala 程序中隐式导入)提供了从 String
到 Seq[Char]
的隐式转换。
val implicitlyConverted: Seq[Char] = "ABC"
因此,它按计划类型检查 运行。
现在让我们看看第二个for-expressions是如何脱糖的:
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(...)
) returns 中的 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 可以给你一个更笼统的答案。
另一种"fix"的方法是改变顺序:
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 中,有些 monad 通过使用 CanBuildFrom
.
来组合
第一个 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"
// ^
回复题主问题:对,第一个生成器"sets the mood"对整个for-expression。请注意,像上面那样的 for-expressions desugared 调用 flatMap
s 和最后的 map
(加上 withFilter
用于守卫).
在您的例子中,第一个 for-expression 脱糖 为以下表达式:
List(1, 2).flatMap(n => "ABC".map(c => s"$c$n"))
这是可行的,因为 Predef
(在每个 Scala 程序中隐式导入)提供了从 String
到 Seq[Char]
的隐式转换。
val implicitlyConverted: Seq[Char] = "ABC"
因此,它按计划类型检查 运行。
现在让我们看看第二个for-expressions是如何脱糖的:
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(...)
) returns 中的 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 可以给你一个更笼统的答案。
另一种"fix"的方法是改变顺序:
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 中,有些 monad 通过使用 CanBuildFrom
.