我想出了以下几点:
def lazyIO[E,A](io: IO[E,A]): IO[Nothing, IO[E, A]] = {
for {
barrier <- Promise.make[Nothing, Unit]
fiber <- (barrier.get *> io).fork
} yield barrier.complete(()) *> putStrLn("getting it") *> fiber.join
}
ZIO 1.0-RC4 的更新版本(具有环境支持):
def lazyIO[R, E, A](io: ZIO[R, E, A]): ZIO[R, Nothing, ZIO[R, E, A]] = {
for {
barrier <- Promise.make[Nothing, Unit]
fiber <- (barrier.await *> io).fork
} yield barrier.succeed(()) *> fiber.join
}
所以这是一个接受 IO 并返回它的惰性版本的 IO。
它的工作原理是启动一个fiber
运行原来的io
,但只有在 Promise 之后(barrier
) 已经完成。
惰性IO首先完成barrier
(如果它是第一个这样做的人,将会解锁fiber
依次运行包装的io
),然后加入fiber
检索计算结果。
有了这个,我可以做
override def run(args: List[String]): IO[Nothing, Main.ExitStatus] = for {
valueFromDb <- lazyIO(longRunningDbAction)
_ <- maybeUseTheValue(valueFromDb)
_ <- maybeNeedItAgain(valueFromDb)
} yield ExitStatus.ExitNow(0)
控制台输出显示,惰性值确实被拉取两次,但只有第一次触发“数据库访问”:
getting it
Calling the database now
The database said 42
getting it
Okay, we need it again here: 42