Aqui está um pedaço de código da documentação para fs2 . A função go
é recursiva. A questão é: como sabemos se a pilha é segura e como raciocinar se alguma função é segura?
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)
Também seria seguro para a pilha se chamarmos go
de outro 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
}
go
para usar, por exemplo, Monad[F]
typeclass - existe um tailRecM
método que permite executar trampolim explicitamente para garantir que a função seja segura para a pilha. Eu posso estar errado, mas sem ele você está confiando em F
ser seguro por si próprio (por exemplo, se implementar trampolim internamente), mas você nunca sabe quem definirá o seu F
, por isso não deve fazer isso. Se você não tiver garantia de que a F
pilha é segura, use uma classe de tipo que for fornecida tailRecM
porque é segura pela pilha por lei.
@tailrec
anotação para funções de gravação de cauda. Para outros casos, não há garantias formais no Scala AFAIK. Mesmo que a função em si seja segura, as outras funções que está chamando podem não ser: /.