【问题标题】:Stackless Scala With Free Monads, complete exampleStackless Scala 和 Free Monads,完整示例
【发布时间】:2013-08-25 09:55:17
【问题描述】:

以下代码改编自一篇论文(R. O. Bjarnason,Stackless Scala With Free Monads)。

论文的标题指出了所提出的数据结构的总体目的——即在恒定堆栈空间中提供递归处理,并让用户以清晰的方式表达递归。

具体来说,我的目标是拥有一个单子结构,它可以在上升时基于恒定堆栈空间中的简单模式匹配对不可变的对树(二叉树)或列表(n-ary-tree)进行结构重写。

sealed trait Free[S[+_], +A]{
  private case class FlatMap[S[+_], A, +B](
    a: Free[S, A],
    f: A => Free[S, B]
  ) extends Free[S, B]

  def map[B](f: A => B): Free[S, B] = this.flatMap((a:A) => Done[S, B](f(a))) 

  def flatMap[B](f: A => Free[S, B]): Free[S, B] = this match { 
    case FlatMap(a, g) => FlatMap(a, (x: Any) => g(x).flatMap(f))
    case x => FlatMap(x, f)
  } 

  @tailrec
  final def resume(implicit S: Functor[S]): Either[S[Free[S, A]], A] = {
    this match {
      case Done(a) => Right(a)
      case More(k) => Left(k)
      case FlatMap(a, f) => a match {
        case Done(a) => f(a).resume
        case More(k) => Left(S.map(k)((x)=>x.flatMap(f)))
        case FlatMap(b, g) => b.flatMap((x: Any) => g(x).flatMap(f)).resume
      }
    }
  }
}

case class Done[S[+_], +A](a: A) extends Free[S, A]

case class More[S[+_], +A](k: S[Free[S, A]]) extends Free[S,A]

trait Functor[F[+_]] {
  def map[A, B](m: F[A])(f: A => B): F[B]
}

type RoseTree[+A] = Free[List, A] 

implicit object listFunctor extends Functor[List] {
  def map[A, B](a: List[A])(f: A => B) = a.map(f)
}
var tree :  Free[List, Int]=  More(List(More(List(More(List(Done(1), Done(2))), More(List(Done(3), Done(4))))), More(List(More(List(Done(5), Done(6))), More(List(Done(7), Done(8)))))))

如何使用Free实现重写?

模式匹配器的钩子在哪里? - 模式匹配器在上升时必须暴露给每个完整的子树!

这可以在 for 块中完成吗?

[问题已编辑。]

【问题讨论】:

    标签: scala monads stackless free-monad


    【解决方案1】:

    更新:下面的答案解决了问题的an earlier version,但大部分仍然相关。


    首先,您的代码无法按原样工作。您可以使所有内容保持不变,或者使用原始论文中的方差注释。为简单起见,我将采用不变的路线(完整示例请参见here),但我也刚刚确认本文中的版本适用于 2.10.2。

    首先回答您的第一个问题:您的二叉树类型与BinTree[Int] 同构。不过,在我们展示这一点之前,我们需要一个用于配对类型的函子:

    implicit object pairFunctor extends Functor[Pair] {
      def map[A, B](a: Pair[A])(f: A => B) = (f(a._1), f(a._2))
    }
    

    现在我们可以使用resume,将BinTree 转换回T

    def from(tree: T): BinTree[Int] = tree match {
      case L(i) => Done(i)
      case F((l, r)) => More[Pair, Int]((from(l), from(r)))
    }
    
    def to(tree: BinTree[Int]): T = tree.resume match {
      case Left((l, r)) => F((to(l), to(r)))
      case Right(i) => L(i)
    }
    

    现在我们可以定义您的示例树:

    var z = 0
    def f(i: Int): T = if (i > 0) F((f(i - 1), f(i - 1))) else { z = z + 1; L(z) }
    val tree = f(3)
    

    让我们通过将每个叶子值替换为包含其前任和后继的树来演示我们的同构和 BinTree 的 monad:

    val newTree = to(
      from(tree).flatMap(i => More[Pair, Int]((Done(i - 1), Done(i + 1))))
    )
    

    重新格式化后,结果将如下所示:

    F((
      F((
        F((
          F((L(0), L(2))),
          F((L(1), L(3)))
        )),
        F((
          F((L(2), L(4))),
          F((L(3), L(5)))
        )),
        ...
    

    等等,正如预期的那样。

    对于您的第二个问题:如果您想对一棵玫瑰树做同样的事情,您只需将这对替换为一个列表(或一个流)。你需要为列表提供一个仿函数实例,就像我们在上面对对所做的那样,然后你得到一棵树,Done(x) 代表叶子,More(xs) 代表分支。


    您的类型需要map 才能使for-comprehension 语法起作用。幸运的是,您可以将map 写成flatMapDone——只需将以下内容添加到Free 的定义中:

    def map[B](f: A => B): Free[S, B] = this.flatMap(f andThen Done.apply)
    

    现在下面和上面的newTree完全一样:

    val newTree = to(
      for {
        i <- from(tree)
        m <- More[Pair, Int]((Done(i - 1), Done(i + 1)))
      } yield m
    )
    

    同样的事情也适用于Free[List, _] 玫瑰树。

    【讨论】:

    • 如何操作树 ... tree.flatMap(i => More[List, Int](List(Done(i - 1), Done(i + 1)))) 然后实现在 for 块内?
    • Function to(tree: BinTree[Int]): T 不是尾递归、see fork 或蹦床,这是 Free 作为广义蹦床的目的。
    • @tod3a:对——tofrom 方法只是用来表明这两种树类型具有相同的形状。 T 不是蹦床的事实是重点。
    • 我们的目标是拥有一个单子结构,该结构提供基于恒定堆栈空间中的简单模式匹配的不可变树的(无限制)重写。使用 Free 而不是 T 将排除在以 ascending 方式遍历时在结构上重写 Tree 的可能性(除了仅 appending leafs)(不变性) .我无法在 Free 中发现 hook 来对 整个 树进行模式匹配并按升序重写树。在 for 块中,只有叶子可以到达; Functor 降序使用并省略了上叉。
    • 我终于明白了,我做出了错误的选择。出于这个目的,免费是错误的单子。它有助于枚举树的叶子,但不会以某种方式枚举子树,这有助于从下到上重建(不可变的)树。所以,@tailrec final def rec(tree: RoseTree[Int]): RoseTree[Int] = tree.resume match { case Left(l) => More[List, Int](efficient_map[RoseTree[Int],RoseTree[Int ]](rec, l)) case Right(i) => Done[List, Int](i) } 会导致错误,如果不引入其他构造就没有帮助。
    猜你喜欢
    • 1970-01-01
    • 2015-05-17
    • 2015-10-02
    • 1970-01-01
    • 1970-01-01
    • 2011-11-14
    • 2012-08-24
    • 1970-01-01
    • 2023-03-22
    相关资源
    最近更新 更多