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

时间:2017-07-13 15:20:56

标签: scala functional-programming tail-recursion trampolines

我正在通过RúnarBjarnason阅读本文Stackless Scala with Free Monad来学习Scala中的蹦床技巧。但我在第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仍然可以炸毁堆栈。这就是我在这里问的原因。

1 个答案:

答案 0 :(得分:0)

它给出了答案:

  

此处还有一个需要考虑的案例。现在可以了   如果FlatMaps的左倾塔是,则恢复溢出堆栈   比调用堆栈高。然后呼叫f(v)将进行呼叫   g(x),它将进行另一次内部调用等......

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