इस कोड पर विचार करें (here से लिया गया है और वर्णों की बजाय बाइट्स का उपयोग करने के लिए संशोधित किया गया है)।ढेर बहने के बिना Scalaz7 Iteratees के साथ IO का उपयोग कैसे करें?
import java.io.{ File, InputStream, BufferedInputStream, FileInputStream }
import scalaz._, Scalaz._, effect._, iteratee.{ Iteratee => I, _ }
import std.list._
object IterateeIOExample {
type ErrorOr[+A] = EitherT[IO, Throwable, A]
def openStream(f: File) = IO(new BufferedInputStream(new FileInputStream(f)))
def readByte(s: InputStream) = IO(Some(s.read()).filter(_ != -1))
def closeStream(s: InputStream) = IO(s.close())
def tryIO[A, B](action: IO[B]) = I.iterateeT[A, ErrorOr, B] {
EitherT(action.catchLeft).map(r => I.sdone(r, I.emptyInput))
}
def enumBuffered(r: => BufferedInputStream) = new EnumeratorT[Int, ErrorOr] {
lazy val reader = r
def apply[A] = (s: StepT[Int, ErrorOr, A]) => s.mapCont(k =>
tryIO(readByte(reader)) flatMap {
case None => s.pointI
case Some(byte) => k(I.elInput(byte)) >>== apply[A]
})
}
def enumFile(f: File) = new EnumeratorT[Int, ErrorOr] {
def apply[A] = (s: StepT[Int, ErrorOr, A]) =>
tryIO(openStream(f)).flatMap(stream => I.iterateeT[Int, ErrorOr, A](
EitherT(
enumBuffered(stream).apply(s).value.run.ensuring(closeStream(stream)))))
}
def main(args: Array[String]) {
val action = (
I.consume[Int, ErrorOr, List] &=
enumFile(new File(args(0)))).run.run
println(action.unsafePerformIO())
}
}
एक सभ्य आकार फ़ाइल (8kb) पर इस कोड चल रहा है एक StackOverflowException पैदा करता है। कुछ खोजों ने यह निष्कर्ष निकाला कि आईओ के बजाय ट्रैम्पोलिन मोनैड का उपयोग करके अपवाद से बचा जा सकता है, लेकिन यह एक महान समाधान की तरह प्रतीत नहीं होता है - कार्यक्रम को पूरा करने के लिए कार्यात्मक शुद्धता बलिदान। इसे ठीक करने का स्पष्ट तरीका आईओ या ट्रैम्पोलिन को दूसरे को लपेटने के लिए एक मोनाड ट्रांसफॉर्मर के रूप में उपयोग करना है, लेकिन मुझे उनमें से किसी के ट्रांसफॉर्मर संस्करण का कार्यान्वयन नहीं मिल रहा है और मैं पर्याप्त कार्यात्मक-प्रोग्रामिंग गुरु नहीं हूं मुझे अपना खुद का लिखना है (एफपी के बारे में और अधिक सीखना इस परियोजना के प्रयोजनों में से एक है, लेकिन मुझे संदेह है कि नए मोनैड ट्रांसफार्मर बनाना इस समय मेरे स्तर से थोड़ा ऊपर है)। मुझे लगता है कि मैं अपने इटेटेट्स के परिणाम को चलाने, चलाने और वापस करने के आसपास एक बड़ी आईओ कार्रवाई को लपेट सकता हूं, लेकिन यह समाधान से अधिक कामकाज की तरह लगता है।
संभवतः कुछ मोनैड को मोनैड ट्रांसफार्मर में परिवर्तित नहीं किया जा सकता है, इसलिए मैं जानना चाहता हूं कि आईओ छोड़ने या ढेर बहने के बिना बड़ी फ़ाइलों के साथ काम करना संभव है, और यदि ऐसा है, तो कैसे?
बोनस प्रश्न: मैं संकेत देने के लिए किसी भी तरह से नहीं सोच सकता कि यह किसी भी त्रुटि का सामना कर रहा है, इसे छोड़कर इसे वापस करने के अलावा, जिससे उन्हें लिखना आसान हो जाता है। उपर्युक्त कोड दिखाता है कि गणनाकर्ताओं में त्रुटियों को संभालने के लिए EitherT का उपयोग कैसे करें, लेकिन यह पुनरावृत्तियों के लिए कैसे काम करता है?
यह आपके लिए उपयोगी हो सकता है: http://termsandtruthconditions.herokuapp.com/blog/2013/03/16/free-monad/ – Impredicative
यह एक अच्छा स्पष्टीकरण है कि मुझे स्टैम्प बहने से बचने के लिए ट्रैम्पोलिन का उपयोग करने की आवश्यकता क्यों है, लेकिन इसमें आईओ और ट्रैम्पोलिन दोनों का उपयोग करने का तरीका शामिल नहीं है। – Redattack34
आईओ पहले से ही trampolined है। – Apocalisp