Scala 如何实现不使用';不要在ZIO中使用大量潜在的heapspace
我知道ZIO维护自己的堆栈,即Scala 如何实现不使用';不要在ZIO中使用大量潜在的heapspace,scala,zio,Scala,Zio,我知道ZIO维护自己的堆栈,即ZIO.internal.FiberContext#stack,它保护递归函数,如 def getNameFromUser(askForName:UIO[String]):UIO[String]= 为了{ resp在递归函数中使用循环。基本上,每次调用getNameFromUser将对象分配给堆时,堆永远无法释放这些对象,因为在t1上创建的对象需要在t2中创建的对象来解析,但t2中的对象需要t3上的对象无限解析。 与循环不同,您应该使用ZIO组合器,就像永远或您可以
ZIO.internal.FiberContext#stack
,它保护递归函数,如
def getNameFromUser(askForName:UIO[String]):UIO[String]=
为了{
resp在递归函数中使用循环。基本上,每次调用getNameFromUser
将对象分配给堆时,堆永远无法释放这些对象,因为在t1上创建的对象需要在t2中创建的对象来解析,但t2中的对象需要t3上的对象无限解析。
与循环不同,您应该使用ZIO组合器,就像永远
或您可以在上找到的任何其他组合器一样
导入zio.Schedule
val getNameFromUser:RIO[控制台,字符串]=用于{
_从上面重写函数的推荐方法是使用
def getNameFromUserSchedule(askForName:UIO[String]):UIO[String]=
askForName.repeat(Schedule.doWhile(u.isEmpty))
这既简洁又可读,并且只消耗恒定数量的ZIO堆栈帧
但是,您不必使用
def getNameFromUser(askForName:UIO[String]):UIO[String]=
为了{
响应
if(resp.isEmpty)getNameFromUser(askForName)else ZIO.success(resp)
}.地图(身份)
唯一的区别是最终的映射(标识)
。当解释此函数生成的ZIO值时,解释器必须在堆栈上推送标识
,计算平面图
,然后应用标识
。但是,要计算平面图
,同样的过程可能会重复,迫使解释器推送尽可能多的标识代码>在堆栈上,因为我们有循环迭代。这有点烦人,但解释器无法知道,它推到堆栈上的函数实际上是标识。您可以通过使用编译器插件消除它们,而无需删除nicefor
语法,该插件能够优化最终的映射(标识)
为理解而脱胶时
如果没有map(identity)
,解释器将执行askForName
,然后使用闭包
resp=>
if(resp.isEmpty)getNameFromUser(askForName)else ZIO.success(resp)
获取用于解释的下一个ZIO值。此过程可能重复任意次数,但解释程序堆栈的大小将保持不变
总而言之,以下是关于ZIO解释器何时使用其内部堆栈的简要讨论:
计算链式flatMap
,如io0.flatMap(f1).flatMap(f2).flatMap(f3)
。要计算这样的表达式,解释器将在堆栈上按f3
,查看io0.flatMap(f1).flatMap(f2)
。然后将f2
放在堆栈上,查看io0.flatMap(f1)
。最后将f1
放在堆栈上,并对io0
进行评估(解释器中存在一个优化,可能会在此处采用快捷方式,但这与讨论无关)。在对io0
进行求值后,从堆栈中弹出f1
,并应用于r0
的结果,给我们一个新的ZIO值,io1=f1(r0)
。现在将io1
求值为r1
,并从堆栈中弹出f2
,以获得下一个ZIO值io2=f2(r1)
。最后,io2
计算为r2
,从堆栈中弹出f3
以获得io3=f3(r2)
和io3
被解释为表达式的最终结果r3
。因此,如果您有一个算法,通过将flatMaps
链接在一起工作,您应该期望ZIO堆栈的最大深度至少是flatMaps
链的长度
计算链式折叠时,如io.foldM(h1,f1).foldM(h2,f2).foldM(h3,f3)
,或链式折叠和链式平面图的混合。如果没有错误,折叠的行为类似于平面图
,因此关于ZIO堆栈的分析非常相似。您应该预计ZIO堆栈的最大深度至少是链的长度
在应用上述规则时,请记住,有许多组合符直接或间接地实现在flatMap
和foldCauseM
之上:
map
、as
、zip
、zipWith
、
、foldLeft
、foreach
都是在flatMap
之上实现的
fold
,foldM
,catchSome
,catchAll
,maperor
是在foldCauseM
之上实现的
最后但并非最不重要的一点:您不应该太担心ZIOs内部堆栈的大小,除非
- 您正在实现一个算法,其中迭代次数可能会变得任意大,而对于中等大小的输入数据,甚至是恒定大小的输入数据
- 您正在遍历不适合内存的非常大的数据结构
- 用户只需很少的努力就可以直接影响堆栈深度(例如,这意味着无需通过网络向您发送大量数据)
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)
}