Đây là một đoạn mã từ tài liệu cho fs2 . Hàm go
được đệ quy. Câu hỏi là làm thế nào để chúng ta biết nếu nó là stack an toàn và làm thế nào để suy luận nếu bất kỳ chức năng nào là stack an toàn?
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)
Nó cũng sẽ được ngăn xếp an toàn nếu chúng ta gọi go
từ một phương thức khác?
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
để sử dụng ví dụ như Monad[F]
typeclass - có tailRecM
phương pháp cho phép bạn thực hiện trampoline một cách rõ ràng để đảm bảo rằng chức năng sẽ được xếp chồng an toàn. Tôi có thể sai nhưng không có nó, bạn đang dựa vào F
việc tự mình xếp chồng an toàn (ví dụ: nếu nó thực hiện trampoline trong nội bộ), nhưng bạn không bao giờ biết ai sẽ xác định bạn F
, vì vậy bạn không nên làm điều này. Nếu bạn không có gì đảm bảo rằng F
stack an toàn, hãy sử dụng một loại lớp cung cấp tailRecM
bởi vì nó là stack an toàn theo luật.
@tailrec
chú thích cho các hàm rec đuôi. Đối với các trường hợp khác, không có đảm bảo chính thức trong Scala AFAIK. Ngay cả khi chức năng đó an toàn, các chức năng khác mà nó gọi có thể không phải là: /.