21

这里有一个很棒的教程,似乎向我暗示 Writer Monad 基本上是一个特殊情况的元组对象,它代表 (A,B) 执行操作。writer 在左侧累积值(即 A),并且 A 具有相应的 Monoid(因此它可以累积或改变状态)。如果 A 是一个集合,那么它会累积。

State Monad 也是一个处理内部元组的对象。它们都可以是 flatMap'd、map'd 等。操作对我来说似乎是一样的。它们有何不同?(请回复一个 scala 示例,我不熟悉 Haskel)。谢谢!

4

2 回答 2

39

您认为这两个单子密切相关的直觉是完全正确的。不同之处在于Writer它的限制要大得多,因为它不允许您读取累积状态(直到您最后兑现)。您可以对 a 中的状态做的唯一事情就是Writer在末尾添加更多内容。

更简洁地说,State[S, A]是 的一种包装器S => (S, A),而Writer[W, A]是 的包装器(W, A)

考虑以下用法Writer

import scalaz._, Scalaz._

def addW(x: Int, y: Int): Writer[List[String], Int] =
  Writer(List(s"$x + $y"), x + y)

val w = for {
  a <- addW(1, 2)
  b <- addW(3, 4)
  c <- addW(a, b)
} yield c

现在我们可以运行计算:

scala> val (log, res) = w.run
log: List[String] = List(1 + 2, 3 + 4, 3 + 7)
res: Int = 10

我们可以做同样的事情State

def addS(x: Int, y: Int) =
  State((log: List[String]) => (log |+| List(s"$x + $y"), x + y))

val s = for {
  a <- addS(1, 2)
  b <- addS(3, 4)
  c <- addS(a, b)
} yield c

接着:

scala> val (log, res) = s.run(Nil)
log: List[String] = List(1 + 2, 3 + 4, 3 + 7)
res: Int = 10

但这有点冗长,我们还可以做很多其他State我们不能做的事情Writer

所以这个故事的寓意是你应该尽可能地使用Writer——你的解决方案会更干净、更简洁,并且你会因为使用了适当的抽象而感到满意。

但是,通常Writer不会为您提供所需的所有功能,并且在这些情况下State会等着您。

于 2014-05-29T22:10:57.913 回答
1

tl;博士状态是读写,而作家是,好吧,只写。

使用 State,您可以访问之前存储的数据,并且可以在当前计算中使用这些数据:

def myComputation(x: A) =
  State((myState: List[A]) => {
        val newValue = calculateNewValueBasedOnState(x,myState)
        (log |+| List(newValue), newValue)
  })

使用 Writer,您可以将数据存储在您无权访问的某个对象中,您只能写入该对象。

于 2017-10-27T07:32:33.657 回答