Aquí hay un fragmento de código de la documentación para fs2 . La función goes recursiva. La pregunta es ¿cómo sabemos si es seguro para la pila y cómo razonar si alguna función es segura para la pila?
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)
¿También sería seguro para la pila si llamamos godesde otro método?
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
}
gopara usar, por ejemplo, Monad[F]typeclass: hay un tailRecMmétodo que le permite realizar el trampolín explícitamente para garantizar que la función sea segura para la pila. Puede que me equivoque, pero sin eso Fdependes de la seguridad de la pila por sí solo (por ejemplo, si implementa el trampolín internamente), pero nunca sabes quién definirá tu F, así que no deberías hacer esto. Si no tiene garantía de que Fsea apilable, use una clase de tipo que proporcione tailRecMporque es apta para la ley.
@tailrecanotaciones para las funciones de grabación de cola. Para otros casos no hay garantías formales en Scala AFAIK. Incluso si la función en sí es segura, las otras funciones que está llamando podrían no ser: /.