2

この質問はこの他の質問に関連していますが、より単純なケースに縮小されています。

次のインポートを想定しています。

import scalaz._, Scalaz._
import Free._, effect._

次のジェネレーターがあります。

val fromOneIO: () => IO[Int] = {
  var i = 0; () => { i += 1; IO(i) }
} 
val fromOne: () => Int = {
  var i = 0; () => { i += 1; i }
}

および次の非末尾再帰定義:

def rec(i: Int): Int = {
  if (i == 0) {
    fromOne()
  } else {
    rec(i - 1) + fromOne()
  }
}
def rec1(i: Int): Trampoline[Int] = {
  if (i == 0) {
    Return(fromOne())
  } else {
    suspend {
      for {
        a <- rec1(i - 1)
        b <- Return(fromOne()): Trampoline[Int]
      } yield a + b
    }
  }
}
def recio(i: Int): Trampoline[IO[Int]] = {
  if (i == 0) {
    Return(fromOneIO())
  } else {
    suspend {
      for {
        ioa <- recio(i - 1)
        iob <- Return(fromOneIO()): Trampoline[IO[Int]]
      } yield {
        for (a <- ioa; b <- iob) yield a + b
      }
    }
  }
}

結果は次のとおりです。

rec(100) // overflows for arg 10000
rec1(10000).run // works
recio(10000).run.unsafePerformIO() //overflows

IOマップ/フラットマップもトランポリンするようにするにはどうすればよいですか? 理解のために、2番目の内部に他のネストされたスタックが作成されているようです。抽出された io 値をTrampolineT使用してサスペンドに再ラップする を作成する必要がありますか?unsafePerformIO

4

1 に答える 1

3

それだけの価値があるので、すでにトランポリンされていIO(_)ますfolone からの提案に加えて、次のように秒を変更することでオーバーフローを回避できます。

val s = for (a <- ioa; b <- iob) yield a + b
val s1 = s.unsafePerformIO()
IO(s1)

またはこのように:

IO(for (a <- ioa; b <- iob) yield a + b).flatMap(identity)

またIO(_)、名前によるパラメータを取るためIO(expr)、 とval e = expr; IO(e)は同じように動作しません。これは溢れます!

val s = for (a <- ioa; b <- iob) yield a + b
IO(s.unsafePerformIO())

したがって、非常に満足のいく答えではありませんが、可能な場合はアプリケーションを使用するか、別の方法でラップしIOてフラット化することが、スタックを吹き飛ばす回避策になるようです。

于 2013-04-24T13:39:13.430 に答える