Hier ist ein Teil des Codes aus der Dokumentation für fs2 . Die Funktion go
ist rekursiv. Die Frage ist, woher wissen wir, ob es stapelsicher ist und wie man begründet, ob eine Funktion stapelsicher ist?
import fs2._
// import fs2._
def tk[F[_],O](n: Long): Pipe[F,O,O] = {
def go(s: Stream[F,O], n: Long): Pull[F,O,Unit] = {
s.pull.uncons.flatMap {
case Some((hd,tl)) =>
hd.size match {
case m if m <= n => Pull.output(hd) >> go(tl, n - m)
case m => Pull.output(hd.take(n.toInt)) >> Pull.done
}
case None => Pull.done
}
}
in => go(in,n).stream
}
// tk: [F[_], O](n: Long)fs2.Pipe[F,O,O]
Stream(1,2,3,4).through(tk(2)).toList
// res33: List[Int] = List(1, 2)
Wäre es auch stapelsicher, wenn wir go
von einer anderen Methode aus aufrufen ?
def tk[F[_],O](n: Long): Pipe[F,O,O] = {
def go(s: Stream[F,O], n: Long): Pull[F,O,Unit] = {
s.pull.uncons.flatMap {
case Some((hd,tl)) =>
hd.size match {
case m if m <= n => otherMethod(...)
case m => Pull.output(hd.take(n.toInt)) >> Pull.done
}
case None => Pull.done
}
}
def otherMethod(...) = {
Pull.output(hd) >> go(tl, n - m)
}
in => go(in,n).stream
}
go
, um z. B. eine Monad[F]
Typklasse zu verwenden. Es gibt eine tailRecM
Methode, mit der Sie das Trampolin explizit ausführen können, um sicherzustellen, dass die Funktion stapelsicher ist. Ich könnte mich irren, aber ohne sie verlassen Sie sich darauf, F
dass Sie selbst stapelsicher sind (z. B. wenn es Trampolin intern implementiert), aber Sie wissen nie, wer Ihr definiert F
, also sollten Sie dies nicht tun. Wenn Sie keine stapelsichere Garantie haben F
, verwenden Sie eine Typklasse, die dies vorsieht, tailRecM
da sie gesetzlich stapelsicher ist.
@tailrec
Anmerkungen für Tail Rec-Funktionen beweisen zu lassen . Für andere Fälle gibt es in der Scala AFAIK keine formellen Garantien. Selbst wenn die Funktion selbst sicher ist, sind die anderen Funktionen, die sie aufruft, möglicherweise nicht: /.