7

Scalaz 对有状态计算提供了一个很好的抽象:ST monad。

ST monad 允许以函数形式捕获副作用计算。

我想在 Haskell 中,使用这样的 monad 是有效实现某些命令式算法的唯一方法。

但在 Scala 中,如有必要,我可以简单地使用可变数据结构。

我发现,使用 Scalaz 的函数概念会带来计算开销。例如看这个问题。因此,如果期望的目标是提高效率,那么使用 ST monad 从功能实现切换到功能实现似乎是不合理的。

我要问的是:

  • 什么时候使用 ST 单子是合理的?
  • 您在哪些情况下使用它并且它似乎是一个不错的选择?
4

1 回答 1

11

正如唐斯图尔特正确指出的那样,您似乎正在寻找 ST 单子而不是状态单子。所以我的回答将是关于这个的。

ST monad 用于允许 Haskell 中的局部可变性,而通常不允许可变性。例如,如果你想编写一个命令式sum函数,你会使用 ST monad。带有 scalaz 的此类函数的示例是(取自此处):

def sumST[S, A](as: List[A])(implicit A: Numeric[A]): ST[S, A] =
  for { n <- newVar(A.zero)
        _ <- as.traverseU(a => n.mod(A.plus(_, a)))
        m <- n.read } yield m

def sum[A : Numeric](as: List[A]): A =
  runST(new Forall[({type λ[S] = ST[S, A]})#λ] {
    def apply[S] = sumST[S, A](as)
  })

显然在 scala 中我们也可以只写:

def sum[A](xs: List[A])(implicit N: Numeric[A]) = {
  var sum = N.zero
  val it = xs.iterator
  while (it.hasNext) {
    sum = N.plus(sum, it.next)
  }
  sum
}

这仍然是引用透明的,因为没有可变状态逃脱函数的范围。在 Haskell 中,它用于这些情况,因为我们根本不能拥有var.

那么我们为什么要在scala中使用ST呢?如果你想处理一个可变结构(如数组)并想保证这个可变结构不会脱离计算范围,但必须先变成一个不可变结构,你可以使用 ST。在 scalaz 你有这种情况,当 ST monad 正在运行时STArray,这将变成一个。ImmutableArray一个很好的例子是scalaz 中的二进制排序示例。值得一读的还有Rúnar Bjarnason关于 ST monad 的博客文章。

于 2013-10-29T12:01:01.470 回答