为什么Scala book中FP的非蹦床IO monad实现会产生StackOverflowerr错误?
我正在学习Scala书中的函数编程。在第13章。外部效应和I/O,第13.3点避免StackOverflower错误,第237页有一个例子,说明了通过本章开发的带有IO monad的简单程序如何引发为什么Scala book中FP的非蹦床IO monad实现会产生StackOverflowerr错误?,scala,functional-programming,Scala,Functional Programming,我正在学习Scala书中的函数编程。在第13章。外部效应和I/O,第13.3点避免StackOverflower错误,第237页有一个例子,说明了通过本章开发的带有IO monad的简单程序如何引发StackOverflower错误 崩溃代码很简单: IO.forever(IO { println("Still going...") }).run 可以找到IO特征和同伴对象的源 我一眼就看不出为什么会抛出StackOverflowerError,所以我仔细研究了这个示例,展开并计算表达式,就像
StackOverflower错误
崩溃代码很简单:
IO.forever(IO { println("Still going...") }).run
可以找到IO特征和同伴对象的源
我一眼就看不出为什么会抛出StackOverflowerError
,所以我仔细研究了这个示例,展开并计算表达式,就像我是Scala运行时一样。经过一些迭代之后,我将回到起点(t.run
)。我无法在调用堆栈上堆积对run
的多个调用:
IO.forever(IO { println("Still going...") }).run
t.run
(a.flatMap(_ => t)).run
(new IO[Unit] {
def run = (_ => t)(self.run).run
}).run
(_ => t)(self.run).run
> "Still going..."
(_ => t)(()).run
t.run
永远
的定义很简单:
def forever[A,B](a: F[A]): F[B] = {
lazy val t: F[B] = a.flatMap(_ => t)
t
}
我在纸笔评估中做错了什么?Scala是否在我的大脑Scala之前评估了lazy val
t
?我认为lazy val
没有问题。相反,当从
(new IO[Unit] {
def run = (_ => t)(self.run).run
}).run
到
实际上,它正在调用
self.run
,同时仍在外部run
评估的上下文中-它尚未从def run
返回我认为lazy val
没有问题。相反,当从
(new IO[Unit] {
def run = (_ => t)(self.run).run
}).run
到
实际上,它正在调用self.run
时,仍然在外部run
计算的上下文中-它还没有从def run
返回,您的意思是:(new IO[Unit]{def run=(=>t)(self.run.run})。run
的计算结果是:>“仍在运行…”/*from self.run*/(new IO)[Unit]{def run=t.run}).run
?在匿名IO trait实例的构造上是否对新IO[Unit]{def run=…}
进行了评估?是的。否。我不知道,根据第3.3.1节,这个术语重写似乎不适合表示任何地方的调用堆栈。方法类型:“一种特殊情况是没有任何参数的方法类型。它们在此处写入=>T。无参数方法名称表达式,每次引用无参数方法名称时都会重新计算。”。如果我没有弄错,则使用无参数方法(new IO[Unit]{def run=…}
,在这种情况下)是的。首先它构造IO[Unit]
实例,然后在其上调用无参数run
方法。在run
主体的求值过程中(即run
仍在堆栈上),它将调用self.run
,然后调用t.run
才有意义。我在评估表达式时就好像它是Haskell一样,我忘记了JVM是基于堆栈的。我从Alvin Alexander的“函数编程,简化”中找到了一个很好的摘录,它解释了JVM调用堆栈对Scala函数的影响。你的意思是:(新IO[Unit]{def run=(=>t)(self.run.run}).run
的计算结果为:“仍在运行…”/*from self.run*/(新IO[Unit]{def run=t.run})。run
是新IO[Unit]{def run=…}
对匿名IO trait实例的构造进行了评估?是的。不是。我不知道,根据第3.3.1节的方法类型,这个术语“重写”似乎不适合表示任何地方的调用堆栈:一种特殊情况是没有任何参数的方法类型。它们写在这里=>T。每次引用无参数方法名称时,都会重新计算无参数方法名称表达式。“。如果我没有弄错,则使用无参数方法(new IO[Unit]{def run=…}
)是的。首先它构造IO[Unit]
实例,然后在其上调用无参数run
方法。在run
主体的求值过程中(即run
仍在堆栈上),它将调用self.run
,然后调用t.run
才有意义。我对表达式的评估就像它是Haskell一样,我忘记了JVM是基于堆栈的。我从Alvin Alexander的“函数编程,简化”中找到了一个很好的摘录,它解释了JVM调用堆栈对Scala函数的影响。