Berikut adalah sepotong kode dari dokumentasi untuk fs2 . Fungsi go
ini bersifat rekursif. Pertanyaannya adalah bagaimana kita tahu apakah itu stack safe dan bagaimana alasannya jika ada fungsi stack safe?
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)
Apakah ini juga akan menjadi tumpukan aman jika kita memanggil go
dari metode lain?
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
untuk menggunakan mis. Monad[F]
Typeclass - ada tailRecM
metode yang memungkinkan Anda untuk melakukan trampolin secara eksplisit untuk menjamin bahwa fungsi tersebut akan menjadi stack safe. Saya mungkin salah tetapi tanpa itu Anda mengandalkan F
tumpukan aman sendiri (misalnya jika itu menerapkan trampolin secara internal), tetapi Anda tidak pernah tahu siapa yang akan menentukan Anda F
, jadi Anda tidak harus melakukan ini. Jika Anda tidak memiliki jaminan bahwa itu F
adalah stack safe, gunakan kelas tipe yang menyediakan tailRecM
karena itu adalah stack-safe oleh hukum.
@tailrec
anotasi untuk fungsi tail rec. Untuk kasus lain tidak ada jaminan formal di Scala AFAIK. Bahkan jika fungsi itu sendiri aman, fungsi lain yang dihubungi mungkin tidak: /.