0Pricing
Scala for Backend Engineering & Functional Programming · 课时

组合 Either

map 与 flatMap

组合 Either 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

组合计算

实际程序会串联多个可能失败的步骤:解析、验证、查找、计算。Either 允许您组合这些步骤,使第一次失败立即终止整个链。

所使用的工具包括 map、flatMap 和 for 推导式。

flatMap 串联 Either

flatMap 会应用一个自身返回 Either 的函数。如果接收者是 Left,则不会调用该函数,而是继续传播这个 Left。

object Main {
  def positive(n: Int): Either[String, Int] =
    if (n > 0) Right(n) else Left("not positive")

  def main(args: Array[String]): Unit = {
    val r = Right(5).flatMap(positive)
    val l = Right(-1).flatMap(positive)
    println(r)
    println(l)
  }
}

短路处理

当您串联多个 flatMap 调用时,第一个 Left 会停止后续所有操作。之后的步骤会被跳过,该错误会成为最终结果。

object Main {
  def step(label: String, n: Int): Either[String, Int] = {
    println(s"running $label")
    Right(n + 1)
  }

  def main(args: Array[String]): Unit = {
    val result = Left("early error").asInstanceOf[Either[String, Int]]
      .flatMap(n => step("A", n))
      .flatMap(n => step("B", n))
    println(result)
  }
}

对 Either 使用 for 推导式

由于 Either 是右偏的,因此可以在 for 推导式中使用它。每个 <- 都会解包一个 Right;任何 Left 都会停止推导式并成为结果。

object Main {
  def parse(s: String): Either[String, Int] =
    s.toIntOption.toRight(s"bad: $s")

  def main(args: Array[String]): Unit = {
    val sum = for {
      a <- parse("3")
      b <- parse("4")
    } yield a + b
    println(sum)
  }
}

失败的 for 推导式

如果推导式中的任何一步产生 Left,整个表达式就是该 Left,后续步骤也不会运行。

object Main {
  def parse(s: String): Either[String, Int] =
    s.toIntOption.toRight(s"bad: $s")

  def main(args: Array[String]): Unit = {
    val sum = for {
      a <- parse("3")
      b <- parse("oops")
      c <- parse("5")
    } yield a + b + c
    println(sum)
  }
}

串联验证步骤

一个实际的处理流程:先解析字符串,再检查业务规则,最后进行转换。每个阶段都返回一个 Either。

object Main {
  def parse(s: String): Either[String, Int] = s.toIntOption.toRight("not a number")
  def checkRange(n: Int): Either[String, Int] =
    if (n >= 1 && n <= 100) Right(n) else Left("out of range")

  def process(s: String): Either[String, Int] =
    parse(s).flatMap(checkRange).map(_ * 10)

  def main(args: Array[String]): Unit = {
    println(process("7"))
    println(process("500"))
    println(process("x"))
  }
}

map 与 flatMap

当函数返回普通值时使用 map。当函数返回另一个 Either 时使用 flatMap,以避免嵌套的 Either[String, Either[String, Int]]。

@main def run(): Unit = {
  val withMap: Either[String, Int] = Right(2).map(_ + 1)
  val nested: Either[String, Either[String, Int]] = Right(2).map(n => Right(n + 1))
  val flat: Either[String, Int] = Right(2).flatMap(n => Right(n + 1))
  println(withMap)
  println(nested)
  println(flat)
}

组合相互独立的值

即使每一步都不依赖前一步,for 推导式仍然适用。只有所有步骤都成功,最终的 yield 才会执行。

object Main {
  def parse(s: String): Either[String, Int] = s.toIntOption.toRight(s"bad: $s")

  def main(args: Array[String]): Unit = {
    val combined = for {
      x <- parse("10")
      y <- parse("20")
      z <- parse("30")
    } yield List(x, y, z).sum
    println(combined)
  }
}

leftMap 风格的错误转换

标准库中的 Either 没有 leftMap,但您可以使用 swap.map(...).swap,或在 fold 内部进行映射来转换错误。这样可以让整个处理流程中的错误类型保持一致。

@main def run(): Unit = {
  val e: Either[String, Int] = Left("low-level error")
  val mapped = e.swap.map(msg => s"context: $msg").swap
  println(mapped)
}

综合运用

一个小型计算器处理流程:解析两个数字并执行除法,将所有失败报告为带有类型的错误。

object Main {
  def parse(s: String): Either[String, Int] = s.toIntOption.toRight(s"bad number: $s")
  def divide(a: Int, b: Int): Either[String, Int] =
    if (b == 0) Left("division by zero") else Right(a / b)

  def calc(x: String, y: String): Either[String, Int] =
    for {
      a <- parse(x)
      b <- parse(y)
      r <- divide(a, b)
    } yield r

  def main(args: Array[String]): Unit = {
    println(calc("20", "4"))
    println(calc("20", "0"))
    println(calc("x", "4"))
  }
}

为什么这很重要

组合 Either 可以实现铁路导向编程:正常路径沿着 Right 前进,任何错误都会转入 Left 轨道并绕过其余步骤。不需要异常,不需要空值检查,一切都只是值。

快速检查

检验您对组合 Either 的理解。

回顾

您已经学习了如何组合 Either:

  • flatMap 会串联可能失败的步骤,并在第一个 Left 处短路。
  • for 推导式让多步骤处理流程更易读。
  • 对普通结果使用 map,对返回 Either 的函数使用 flatMap。
  • 使用 swap.map(...).swap 转换错误。

常见问题解答

「组合 Either」课时是免费的吗?

是的 — 「组合 Either」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

「组合 Either」这节课中我会学到什么?

map 与 flatMap 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「组合 Either」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?

能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 Either 表示错误
  2. Try、Success、Failure
  3. 组合 Either
  4. 类型之间的转换
← 返回 Scala for Backend Engineering & Functional Programming