我正在通過閱讀RúnarBjarnason的Stackless Scala with Free Monad這篇文章來學習斯卡拉的蹦牀技巧。但是我陷入了第4.3節「容易出錯」。爲什麼嵌套的FlatMaps可能會炸燬Scala中的堆棧?
有一件事讓我很困惑,f(x)
可以直接觸發另一個內部呼叫給定FlatMap(x, f)
。 resume
已經是尾部遞歸,所以它必須發生在一個resume
調用中。但是resume
中的所有封裝函數都應該導致一個蹦牀實例。所以我找不到這樣的情況,堆棧仍然可能被炸燬。
任何人都可以舉一個例子來解釋「角落案例」嗎?謝謝。
=============
這是案件的背景,你還沒有看到真棒紙:
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
仍然可以炸燬堆棧。這就是我在這裏問的原因。
這是一個有趣的問題的構造並且我認爲文章中闡述的問題的一個[MCVE]可能有助於提供一個答案(如果他們還沒有閱讀,我不認爲人們會閱讀一個8頁的PDF來回答這個問題)。 –
@YuvalItzchakov你是對的。我正在使用我的手機發布問題。一旦我回到我的鍵盤,將更新細節。謝謝。 – SanCoder