Voici un morceau de code de la documentation de fs2 . La fonction go
est 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 go
d'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
}
go
pour utiliser, par exemple, une Monad[F]
classe de types - il existe une tailRecM
mé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 F
pile est sûre, utilisez une classe de type qui fournit tailRecM
car elle est empilable par la loi.
@tailrec
annotation 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: /.