Я придумал следующее:
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
, но только после того, как Обещание (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