【问题标题】:Use flatMap on List[Either] in Scala在 Scala 中的 List[Either] 上使用 flatMap
【发布时间】:2017-01-18 03:39:21
【问题描述】:

Either 从 Scala 2.12 开始是右偏的,这允许它在没有投影的情况下用于 for/yield 块,就像Option 一样。但显然,当与flatMap 一起使用时,这还不足以表现得像Option

object Main {

  def main(args: Array[String]): Unit = {

    val nums = List.range(1,10)

    println(nums.flatMap(evenOption))
    println(nums.flatMap(evenEither)) // fails

  }

  def evenOption(x: Int): Option[Int]       = if (x % 2 == 0) Some(x) else None
  def evenEither(x: Int): Either[String, Int] = if (x % 2 == 0) Right(x) else Left("not even")

}

我的最小范畴理论知识让我认为Either 不是单子,因此这会失败吗?或者我怎样才能使上面的例子工作?

【问题讨论】:

  • 这个问题可能与 Either 没有扩展 TraversableOnce 的事实有关?

标签: scala functional-programming


【解决方案1】:

这与是否是单子无关。当您在某个数据结构上执行flatMap 方法时,您传入的函数必须返回该数据结构的实例。因此,当您对选项进行平面映射时,您的函数必须返回一个选项。如果你在 Future 上进行平面映射,你的函数必须返回一个 Future。列表也是如此:列表上的平面映射必须返回列表本身。那么为什么你的List.flatMap(Option) 有效而List.flatMap(Either) 无效呢?因为存在从 Option 到 Iterable (Option.option2Iterable) 的隐式转换,并且该转换发生在您的示例中。 Either 数据类型没有这种转换(除非您自己创建它)。

【讨论】:

  • for/yield 如何进行平面映射?例如:for { n <- nums; x <- evenEither(n) } yield n
  • 每个生成器 (<-) 将被转换为 flatMap 操作,最后的 yield 将被转换为 map 操作。有关更多详细信息,您应该查看官方文档:docs.scala-lang.org/tutorials/FAQ/yield.html。如果您还有其他问题,请随时提问。
【解决方案2】:

没有将List[Either[String,Int]] 扁平化为List[Int] 的隐含规则,因此您必须提供这样做的方法。

nums.map(evenEither).flatten {case Right(e) => List(e)
                              case _ => List()}

但这可以更直接一点。

nums.collect{case x if evenEither(x).isRight => x}

【讨论】:

    猜你喜欢
    • 2015-01-05
    • 2020-07-22
    • 1970-01-01
    • 2018-07-02
    • 1970-01-01
    • 2018-05-07
    • 1970-01-01
    • 2019-10-23
    • 1970-01-01
    相关资源
    最近更新 更多