Вот фрагмент кода из документации для fs2 . Функция go
рекурсивная. Вопрос в том, как узнать, безопасен ли он для стека, и как определить, является ли какая-либо функция безопасной для стека?
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)
Будет ли это также безопасно для стека, если мы вызовем go
другой метод?
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
для использования, например, Monad[F]
typeclass - есть tailRecM
метод, позволяющий явно выполнять батут, чтобы гарантировать, что функция будет безопасна для стека. Возможно, я ошибаюсь, но без этого вы полагаетесь на F
то, что сами по себе безопасны в стеке (например, если он реализует батут внутри), но вы никогда не знаете, кто определит ваше F
, поэтому вам не следует этого делать. Если у вас нет гарантии, что F
это стек безопасно, используйте класс типов, который обеспечивает, tailRecM
потому что это безопасно по стеку по закону.
@tailrec
аннотацией для функций хвостовой записи. Для других случаев нет никаких официальных гарантий в Scala AFAIK. Даже если сама функция безопасна, другие функции, которые она вызывает, могут не быть: /.