Red*_*k34 11 scala scalaz iterate scalaz7
考虑这段代码(从这里获取并修改为使用字节而不是字符行).
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())
}
}
Run Code Online (Sandbox Code Playgroud)
在一个体面大小的文件(8kb)上运行此代码会产生StackOverflowException.一些搜索发现可以通过使用Trampoline monad而不是IO来避免异常,但这似乎不是一个很好的解决方案 - 牺牲功能纯度来完成程序的完成.解决这个问题的明显方法是使用IO或Trampoline作为Monad Transformer来包装另一个,但是我找不到其中任何一个的变换器版本的实现,而且我还不够功能编程大师知道怎么写我自己(了解更多关于FP是这个项目的目的之一,但我怀疑创建新的monad变换器目前有点高于我的水平).我想我可以围绕创建,运行和返回我的迭代的结果包装一个大的IO动作,但这感觉更像是一种解决方法而不是解决方案.
大概有些monad无法转换为monad变换器,所以我想知道是否可以使用大文件而不丢弃IO或溢出堆栈,如果是这样,怎么样?
额外的问题:我想不出任何方法让迭代器发出信号,表示它在处理时遇到错误,除非让它返回Either,这使得组合它们变得不那么容易.上面的代码显示了如何使用EitherT来处理枚举器中的错误,但是这对迭代器有什么作用?
在创建异常并在代码的各个位置打印它们的堆栈长度之后,我觉得您的代码没有溢出。一切似乎都以恒定的堆栈大小运行。所以我寻找其他地方。最终我复制了实现consume并添加了一些堆栈深度打印并确认它在那里溢出。
所以这会溢出:
(I.consume[Int, Id, List] &= EnumeratorT.enumStream(Stream.fill(10000)(1))).run
Run Code Online (Sandbox Code Playgroud)
但是,我后来发现这不是:
(I.putStrTo[Int](System.out) &= EnumeratorT.enumStream(Stream.fill(10000)(1)))
.run.unsafePerformIO()
Run Code Online (Sandbox Code Playgroud)
putStrTo使用foldM并且以某种方式不会导致溢出。所以我想知道是否consume可以在foldM. 我只是从 Consumer 中复制了一些内容并进行了调整,直到编译完成:
def consume1[E, F[_]:Monad, A[_]:PlusEmpty:Applicative]: IterateeT[E, F, A[E]] = {
I.foldM[E, F, A[E]](PlusEmpty[A].empty){ (acc: A[E], e: E) =>
(Applicative[A].point(e) <+> acc).point[F]
}
}
Run Code Online (Sandbox Code Playgroud)
它成功了!打印一长串整数。