如何在 ZIO 中实现不使用潜在大量堆空间的循环

Mat*_*ger 4 scala 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
Run Code Online (Sandbox Code Playgroud)

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

tox*_*unk 5

您正在递归函数中使用循环。基本上,每次调用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))
Run Code Online (Sandbox Code Playgroud)

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

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)

}
Run Code Online (Sandbox Code Playgroud)

但是请注意,我fork in run := true在你的build.sbt然后你还需要按照 sbt 文档中的run / connectInput := true解释添加