这是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中没有正式的保证。即使函数本身是安全的,它调用的其他函数也可能不是:/。