首页 > 解决方案 > 如何在 ZIO 中实现不使用潜在大量堆空间的循环

问题描述

我知道 ZIO 维护自己的堆栈,即zio.internal.FiberContext#stack保护递归函数,例如

def getNameFromUser(askForName: UIO[String]): UIO[String] =
  for {
    resp <- askForName
    name <- if (resp.isEmpty) getNameFromUser(askForName) else ZIO.succeed(resp)
  } yield name

从堆栈溢出。但是,它们仍然会占用 ZIO 解释器堆栈中的空间,这可能会导致OutOfMemoryError非常深的递归。你将如何getNameFromUser从上面重写函数,即使askForName效果返回空字符串很长一段时间也不会破坏堆?

标签: scalazio

解决方案


您正在递归函数中使用循环。基本上,每次您调用时,getNameFromUser您都在将对象分配给堆,堆永远无法释放这些对象,因为您在 t1 上创建的对象需要在 t2 中创建的对象来解析,但来自 t2 的对象需要在 t3 上的对象来解析无止境。

您应该使用 ZIO 组合器而不是循环,就像forever您可以在Schedule上找到的任何其他组合器一样

 import zio.Schedule

 val getNameFromUser: RIO[Console, String] = for {
  _    <- putStrLn("Waht is your name")
  name <- zio.console.getStrLn
 } yield name

 val runUntilNotEmpty = Schedule.doWhile[String](_.isEmpty)

 rt.unsafeRun(getNameFromUser.repeat(runUntilNotEmpty))

[编辑] 添加一个不同的示例因为你真正需要的是:

import zio._
import zio.console._
import scala.io.StdIn

object ConsoleEx extends App {

  val getNameFromUser = for {
    _    <- putStrLn("What is your name?")
    name <- getStrLn
    _    <- putStr(s"Hello, $name")
  } yield ()

  override def run(args: List[String]) =
    getNameFromUser.fold(t => {println(t); 1}, _ => 0)

}

但是,请注意,我已经fork in run := true在您的build.sbtthen 中,您还需要按照 sbt 文档中的run / connectInput := true说明添加


推荐阅读