与 Option 和 Future 一起使用
单子式串联
与 Option 和 Future 一起使用 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。
超越集合
for 推导式并不局限于列表。任何提供 map 和 flatMap 的类型都可以使用,包括 Option、Either、Try 和 Future。这称为单子链式调用。
object Main {
def main(args: Array[String]): Unit = {
val sum = for {
a <- Some(10)
b <- Some(20)
} yield a + b
println(sum)
}
}Option 遇到 None 会提前终止
对于 Option,只有当每个生成器都是 Some 时,推导式才会产生结果。如果其中任何一个是 None,整个结果就是 None,后续步骤也会被跳过。
object Main {
def main(args: Array[String]): Unit = {
val ok = for {
a <- Some(2)
b <- Some(3)
} yield a * b
val missing = for {
a <- Some(2)
b <- None: Option[Int]
} yield a * b
println(ok)
println(missing)
}
}串联可选查找
一种常见模式是:在 Map 中查找键,而每次查找都会返回一个 Option。只有所有查找都成功时,for 推导式才会产生值。
object Main {
def main(args: Array[String]): Unit = {
val prices = Map("apple" -> 3, "bread" -> 5)
val total = for {
a <- prices.get("apple")
b <- prices.get("bread")
} yield a + b
println(total)
val missing = for {
a <- prices.get("apple")
c <- prices.get("milk")
} yield a + c
println(missing)
}
}为什么它优于嵌套的 if-else
如果不使用 for 推导式,您就需要嵌套模式匹配或空值检查。单子形式将那座金字塔压平成清晰的线性序列。
object Main {
def parse(s: String): Option[Int] = s.toIntOption
def main(args: Array[String]): Unit = {
val result = for {
x <- parse("40")
y <- parse("2")
} yield x + y
println(result)
}
}Guard 同样适用于 Option
Option 推导式中的守卫会在条件不满足时将值转换为 None,从而充当内联验证。
object Main {
def main(args: Array[String]): Unit = {
def validate(age: Int): Option[Int] =
for {
a <- Some(age)
if a >= 18
} yield a
println(validate(25))
println(validate(15))
}
}引入 Future
Future 表示一个稍后才能获得的值。和 Option 一样,它也有 map 和 flatMap,因此 for 推导式可以按顺序执行异步步骤。
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._
object Main {
def main(args: Array[String]): Unit = {
val f = for {
a <- Future(10)
b <- Future(20)
} yield a + b
println(Await.result(f, 2.seconds))
}
}Future 在这里按顺序运行
当 for 推导式中的一个生成器依赖另一个生成器时,Future 会按顺序运行,因为每个 flatMap 都会等待前一步的结果。
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._
object Main {
def main(args: Array[String]): Unit = {
val f = for {
a <- Future(5)
b <- Future(a * 2)
} yield a + b
println(Await.result(f, 2.seconds))
}
}并行运行 Future
若要并行运行 Future,请在 for 推导式之前启动它们,然后再进行组合。独立的工作会彼此重叠,而不是相互等待。
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._
object Main {
def main(args: Array[String]): Unit = {
val fa = Future(100)
val fb = Future(200)
val combined = for {
a <- fa
b <- fb
} yield a + b
println(Await.result(combined, 2.seconds))
}
}使用 Either 表示更丰富的错误
Either[E, A] 在失败时携带错误值。在 for 推导式中,第一个 Left 会提前终止计算并被返回。
object Main {
def parse(s: String): Either[String, Int] =
s.toIntOption.toRight(s"not a number: $s")
def main(args: Array[String]): Unit = {
val good = for { a <- parse("3"); b <- parse("4") } yield a + b
val bad = for { a <- parse("3"); b <- parse("x") } yield a + b
println(good)
println(bad)
}
}相同的形式,适用于多种类型
请注意,无论使用 Option、Either、Try 还是 Future,推导式的外观都完全相同。不同之处仅在于失败语义。这种统一性正是单子链式调用的强大之处。
import scala.util.Try
object Main {
def main(args: Array[String]): Unit = {
val result = for {
a <- Try("21".toInt)
b <- Try("2".toInt)
} yield a * b
println(result)
}
}不允许混合类型
同一个推导式中的所有生成器必须属于同一个单子。您不能直接将 Option 生成器与 Future 生成器混用;请先转换其中一个(例如使用 Future.fromTry,或将 Option 包装起来)。
object Main {
def main(args: Array[String]): Unit = {
// Convert the Option to keep types consistent
val maybe: Option[Int] = Some(7)
val result = for {
a <- maybe
b <- maybe.map(_ + 1)
} yield a + b
println(result)
}
}快速检查
在 Option for 推导式中,如果某个生成器产生 None,会发生什么?
回顾
您学习了使用 for 推导式进行单子链式调用:
- 适用于任何具有
map和flatMap的类型 Option遇到None会提前终止Either和Try在失败时提前终止,并携带错误信息Future会按顺序执行异步步骤;若要并行执行,请尽早启动它们- 所有生成器都必须共享相同的单子类型
常见问题解答
「与 Option 和 Future 一起使用」课时是免费的吗?
是的 — 「与 Option 和 Future 一起使用」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。
「与 Option 和 Future 一起使用」这节课中我会学到什么?
单子式串联 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「与 Option 和 Future 一起使用」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?
能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。