为什么Scala书中FP的非蹦床IO monad实现产生StackOverflowError?

时间:2018-01-27 10:15:53

标签: scala functional-programming

我正在阅读Scala中的函数式编程一书。在第13章外部效果和I / O,第13.3节避免StackOverflowError,第237页中,有一个示例说明了通过本章开发的带有IO monad的简单程序如何激发StackOverflowError

崩溃的代码很简单:

IO.forever(IO { println("Still going...") }).run

可以找到IO特征和伴随对象的来源here

我乍看之下为什么抛出StackOverflowError,所以我通过示例,扩展和评估表达式,就好像我是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

forever的定义很简单:

def forever[A,B](a: F[A]): F[B] = {
  lazy val t: F[B] = a.flatMap(_ => t)
  t
}

我的铅笔和纸张评估中我做错了什么? Scala是否在之前评估 my-brain-Scala lazy val t

1 个答案:

答案 0 :(得分:2)

我认为lazy val存在问题。相反,您只是在从

开始时隐式应用尾调用优化
(new IO[Unit] {
  def run = (_ => t)(self.run).run
}).run

(_ => t)(self.run).run

虽然实际上它仍然在外部self.run评估的上下文中调用run,但它尚未从def run返回。