加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

如何使用IO与Scalaz7 Iteratees不溢出堆栈?

发布时间:2020-12-16 21:33:18 所属栏目:安全 来源:网络整理
导读:考虑这个代码(取自 here并修改为使用字节而不是字符行). import java.io.{ File,InputStream,BufferedInputStream,FileInputStream }import scalaz._,Scalaz._,effect._,iteratee.{ Iteratee = I,_ }import std.list._object IterateeIOExample { type Error
考虑这个代码(取自 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.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读