scala - 为什么嵌套的 FlatMaps 会在 Scala 中炸毁堆栈?

标签 scala functional-programming tail-recursion trampolines

我正在通过阅读这篇论文来学习 Scala 中的 Trampoline 技巧 Stackless Scala with Free Monad ,作者:Rúnar Bjarnason。但是我陷入了第 4.3 节“容易出错的事情”。

让我感到困惑的是 f(x) 是如何在给定 FlatMap(x, f) 的情况下直接触发另一个内部调用的。 resume 已经是一个尾递归,因此它必须在一次 resume 调用中发生。但是 resume 中的所有包装函数都应该生成一个 Trampoline 实例。所以我找不到堆栈仍然会被炸毁的情况。

任何人都可以举个例子来解释“极端情况”吗?谢谢。

=============

如果您还没有看到这篇很棒的论文,这是背景:

Scala 编译器只能应用一个TCO在本地/最终尾部位置自调用函数上。所以 Trampoline 被带来了,用于将堆栈转换为堆。因此在论文中,针对不同的用途声明了三个 Trampoline 实例。 Done 用于包装最终结果。 More 表示下一步要计算。而FlatMap表示下一步计算之后还有一件事要做。因此,在为其定义了一个bind 操作之后,Trampoline 就变成了一个Monad。请参阅下面的代码(来自论文):

```

sealed trait Trampoline[+A] {
    final def resume: A = this match {
      case Done(v) => v
      case More(k) => k().resume
      case FlatMap(a, f) => a match {
        case Done(v) => f(v).resume
        case More(k) => (FlatMap(k(), f): Trampoline[A]).resume
        case FlatMap(b, g) => (FlatMap(b, (x: Any) => FlatMap(g(x), f)): Trampoline[A]).resume
      }
    }
  }

  case class More[+A](k: () => Trampoline[A])
    extends Trampoline[A]

  case class Done[+A](v: A)
    extends Trampoline[A]

  case class FlatMap[A, +B](sub: Trampoline[A], k: A => Trampoline[B]) extends Trampoline[B]

```

一切对我来说都很有意义,直到它说嵌套的 FlatMap 仍然可以炸毁堆栈。这就是我在这里问的原因。

最佳答案

它在纸上给出了答案:

There is one more corner case to consider here. It’s now possible for resume to overflow the stack if the left-leaning tower of FlatMaps is taller than the call stack. Then the call f(v) will make the call g(x), which will make another inner call, etc...

注意构造函数 FlatMap (b , x => FlatMap (g( x), f )) 立即调用该函数

关于scala - 为什么嵌套的 FlatMaps 会在 Scala 中炸毁堆栈?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45084967/

相关文章:

scala - 在 scala 中编写 apply 风格的函数

java - 从 Scala 到 Java 的翻译

scala - 将 DataFrame 映射到案例类时,为什么 Some(null) 会转换为 None

functional-programming - 为什么 "Algebraic data type"在名称中使用 "Algebraic"?

Scala "functional"将 'bad nesting"的序列转换为格式良好的 XML 的方法

java - 流式传输时重新抛出异常

scala - 我可以将对象的实例方法传递给期望在 Scala 中进行回调的方法吗?

algorithm - 理解递归

java - 这是尾递归吗?

optimization - 函数式编程中的高效递归与不同范式中的低效递归