如何使用IO与Scalaz7 Iteratees不溢出堆栈?
考虑这个代码(取自
here并修改为使用字节而不是字符行).
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,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,A]) => tryIO(openStream(f)).flatMap(stream => I.iterateeT[Int,A]( EitherT( enumBuffered(stream).apply(s).value.run.ensuring(closeStream(stream))))) } def main(args: Array[String]) { val action = ( I.consume[Int,List] &= enumFile(new File(args(0)))).run.run println(action.unsafePerformIO()) } } 在一个体面的文件(8kb)上运行这个代码会产生一个StackOverflowException.有些搜索表明,通过使用蹦床monad而不是IO可以避免异常,但这似乎不是一个很好的解决方案 – 牺牲功能纯度来完成程序的完成.解决这个问题的明显方法是使用IO或蹦床作为Monad Transformer来包装另一个,但是我找不到其中任何一个的变压器版本的实现,而且我还没有足够的功能编程大师知道如何写我自己(了解更多关于FP是这个项目的目的之一,但我怀疑创建新的单体变压器有点高于我的水平在此刻).我想我可以围绕创建,运行和返回我的迭代的结果来包装一个大的IO操作,但是比解决方案更像是一个解决方法. 大概有些monads不能转换为monad变压器,所以我想知道是否可以使用大文件,而不会丢弃IO或溢出堆栈,如果是,怎么办? 奖金问题:我不能想到迭代的任何方式来表示在处理过程中遇到错误,除非它返回任何一种,这使得它不容易组合它们.上面的代码显示了如何使用EitherT来处理枚举器中的错误,但是如何在迭代中起作用? 解决方法
创建异常并在代码的不同位置打印堆栈长度后,我觉得你的代码没有溢出.所有似乎都以不变的堆栈大小运行.所以我寻找其他地方.最终我复制了消费的实现,并添加了一些堆栈深度打印,并确认它在那里溢出.
所以这样溢出: (I.consume[Int,Id,List] &= EnumeratorT.enumStream(Stream.fill(10000)(1))).run 但是,我发现这不是: (I.putStrTo[Int](System.out) &= EnumeratorT.enumStream(Stream.fill(10000)(1))) .run.unsafePerformIO() putStrTo使用foldM,不知何故不会导致溢出.所以我想知道消费是否可以用foldM来实现.我只是从消费中复制了一些东西,并进行了调整,直到编译完成: def consume1[E,F[_]:Monad,A[_]:PlusEmpty:Applicative]: IterateeT[E,F,A[E]] = { I.foldM[E,A[E]](PlusEmpty[A].empty){ (acc: A[E],e: E) => (Applicative[A].point(e) <+> acc).point[F] } } 它工作!打印一长串的ints. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |