Scala 使用状态monad遍历时如何处理嵌套结构

Scala 使用状态monad遍历时如何处理嵌套结构,scala,stack-overflow,scalaz,state-monad,Scala,Stack Overflow,Scalaz,State Monad,我有一个嵌套结构,我正在使用scalaz状态monad将其转换为XML。在我必须处理多层嵌套结构之前,这种方法很有效。下面是一个与我正在做的类似的简化示例。鉴于以下ADT: sealed trait Nested case object Leaf extends Nested case class Foo(inner: Nested) extends Nested case class Bar(inner: Nested) extends Nested 我使用状态monad编写一个转换器对象(

我有一个嵌套结构,我正在使用scalaz状态monad将其转换为XML。在我必须处理多层嵌套结构之前,这种方法很有效。下面是一个与我正在做的类似的简化示例。鉴于以下ADT:

sealed trait Nested
case object Leaf extends Nested
case class Foo(inner: Nested) extends Nested
case class Bar(inner: Nested) extends Nested
我使用状态monad编写一个转换器对象(假设Scalaz7和以下导入
import scalaz.{Node=>\uu,\u};import scalaz.\uu;import scala.xml.\u
):

我的问题是-在
scalaz.stateT
中避免堆栈溢出的最佳方法是什么?我希望继续使用state monad,就像在我的真实示例中一样,如果它使XML序列化逻辑更易于遵循和排除故障(实际的输入结构是从实时调试会话中检索到的JDI镜像,内部值是嵌套的字段值)

编辑:要删除嵌套堆栈问题,请执行以下操作:

import util.control.TailCalls
def nested2(n: Int, acc: Nested = Leaf): TailCalls.TailRec[Nested] =
  if (n == 0) TailCalls.done(acc)
  else TailCalls.tailcall(nested2(n - 1, if (n % 2 == 0) Bar(acc) else Foo(acc)))

蹦床可以帮助您避免堆栈溢出。首先,对于相同的设置:

sealed trait Nested
case object Leaf extends Nested
case class Foo(inner: Nested) extends Nested
case class Bar(inner: Nested) extends Nested

import scalaz.{Node => _, _}; import Scalaz._;
import scala.util.control.TailCalls, scala.xml._

case class Parents(foos: Int, bars: Int)

def nested(n: Int, acc: Nested = Leaf): TailCalls.TailRec[Nested] =
  if (n == 0) TailCalls.done(acc) else TailCalls.tailcall(
    nested(n - 1, if (n % 2 == 0) Bar(acc) else Foo(acc))
  )
一些稍有不同的类型别名:

type TrampolinedState[S, A] = StateT[Free.Trampoline, S, A]
type ParentsS[A] = TrampolinedState[Parents, A]
为了方便起见,我们将导入我们的
MonadState
实例的方法:

val monadState = MonadState[TrampolinedState, Parents]
import monadState._
其余部分实际上更加简洁,因为我们不需要
put
等上的类型参数:

def convertFoo(foo: Foo): ParentsS[Seq[Node]] = for {
  parents <- init
  _ <- put(Parents(parents.foos + 1, parents.bars))
  inner <- convert(foo.inner)
  _ <- put(parents)
} yield <foo count={ parents.foos.toString }/>.copy(child=inner)

def convertBar(bar: Bar): ParentsS[Seq[Node]] = for {
  parents <- init
  _ <- put(Parents(parents.foos, parents.bars + 1))
  inner <- convert(bar.inner)
  _ <- put(parents)
} yield <bar count={ parents.bars.toString }/>.copy(child=inner)

def convert(nested: Nested): ParentsS[Seq[Node]] = nested match {
  case Leaf => Seq[Node]().point[ParentsS]
  case foo@Foo(_) => convertFoo(foo)
  case bar@Bar(_) => convertBar(bar)
}

这远远超过了香草
状态
解决方案在我的机器上开始阻塞的程度。

我想起了我已添加书签的这条线程。我刚刚注意到你开始使用它-我一直使用StateT,但当我知道我将要遍历200多个对象时,结果就不那么优雅了。我只是通过运行n=1000的嵌套方法(不使用任何Scalaz代码)获得了StackOverflow。@paragratic,使用我刚刚添加的蹦床
nested2
。我想我的问题的答案也是蹦床
convert
,但我并不清楚如何优雅地完成。谢谢!但愿我能+10这个。在我之前执行
List[a].transverse[({typeλ[α]=State[S,α]})#λ,a]
的几个地方,我使用这种通用方法来防止这种情况。花了一点时间才弄明白如何使用Scalaz 6实现这一点,但我最终还是实现了。
val monadState = MonadState[TrampolinedState, Parents]
import monadState._
def convertFoo(foo: Foo): ParentsS[Seq[Node]] = for {
  parents <- init
  _ <- put(Parents(parents.foos + 1, parents.bars))
  inner <- convert(foo.inner)
  _ <- put(parents)
} yield <foo count={ parents.foos.toString }/>.copy(child=inner)

def convertBar(bar: Bar): ParentsS[Seq[Node]] = for {
  parents <- init
  _ <- put(Parents(parents.foos, parents.bars + 1))
  inner <- convert(bar.inner)
  _ <- put(parents)
} yield <bar count={ parents.bars.toString }/>.copy(child=inner)

def convert(nested: Nested): ParentsS[Seq[Node]] = nested match {
  case Leaf => Seq[Node]().point[ParentsS]
  case foo@Foo(_) => convertFoo(foo)
  case bar@Bar(_) => convertBar(bar)
}
convert(nested(2000).result).apply(Parents(0, 0)).run