यहाँ 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]
टाइपकास्टल का उपयोग करने के लिए फिर से लिख सकते हैं - ऐसी tailRecM
विधि है जो आपको स्पष्ट रूप से गारंटी देने के लिए ट्रम्पोलिन प्रदर्शन करने की अनुमति देती है कि फ़ंक्शन सुरक्षित हो जाएगा। मैं गलत हो सकता हूं, लेकिन इसके बिना आप F
अपने दम पर सुरक्षित होने पर भरोसा कर रहे हैं (जैसे अगर यह आंतरिक रूप से trampoline को लागू करता है), लेकिन आप कभी नहीं जानते कि कौन आपके परिभाषित करेगा F
, इसलिए आपको ऐसा नहीं करना चाहिए। यदि आपके पास कोई गारंटी नहीं है कि F
स्टैक सुरक्षित है, तो एक प्रकार के वर्ग का उपयोग करें जो प्रदान करता है tailRecM
क्योंकि यह कानून द्वारा स्टैक-सुरक्षित है।
@tailrec
को टेल री फ़ंक्शन के लिए एनोटेशन के साथ यह साबित करना आसान है । अन्य मामलों के लिए स्काला AFAIK में कोई औपचारिक गारंटी नहीं है। यहां तक कि अगर फ़ंक्शन स्वयं सुरक्षित है, तो अन्य फ़ंक्शन जो कॉल कर रहे हैं वे नहीं हो सकते हैं: /।