Voici un morceau de code de la documentation de fs2 . La fonction goest récursive. La question est de savoir comment savoir si elle est sans danger pour la pile et comment raisonner si une fonction est sans danger pour la pile?
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)
Serait-il également sûr de la pile si nous appelons god'une autre méthode?
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
}
gopour utiliser, par exemple, une Monad[F]classe de types - il existe une tailRecMméthode vous permettant d'exécuter le trampoline de manière explicite pour garantir que la fonction sera sécurisée. Je peux me tromper, mais sans cela, vous comptez Fêtre sûr de la pile (par exemple, s'il implémente le trampoline en interne), mais vous ne savez jamais qui définira votre F, donc vous ne devriez pas faire cela. Si vous n'avez aucune garantie que la Fpile est sûre, utilisez une classe de type qui fournit tailRecMcar elle est empilable par la loi.
@tailrecannotation pour les fonctions d'enregistrement de queue. Pour les autres cas, il n'y a aucune garantie formelle dans Scala AFAIK. Même si la fonction elle-même est sûre, les autres fonctions qu'elle appelle peuvent ne pas être: /.