这里有一个很棒的教程,似乎向我暗示 Writer Monad 基本上是一个特殊情况的元组对象,它代表 (A,B) 执行操作。writer 在左侧累积值(即 A),并且 A 具有相应的 Monoid(因此它可以累积或改变状态)。如果 A 是一个集合,那么它会累积。
State Monad 也是一个处理内部元组的对象。它们都可以是 flatMap'd、map'd 等。操作对我来说似乎是一样的。它们有何不同?(请回复一个 scala 示例,我不熟悉 Haskel)。谢谢!
这里有一个很棒的教程,似乎向我暗示 Writer Monad 基本上是一个特殊情况的元组对象,它代表 (A,B) 执行操作。writer 在左侧累积值(即 A),并且 A 具有相应的 Monoid(因此它可以累积或改变状态)。如果 A 是一个集合,那么它会累积。
State Monad 也是一个处理内部元组的对象。它们都可以是 flatMap'd、map'd 等。操作对我来说似乎是一样的。它们有何不同?(请回复一个 scala 示例,我不熟悉 Haskel)。谢谢!
您认为这两个单子密切相关的直觉是完全正确的。不同之处在于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
会等着您。
tl;博士状态是读写,而作家是,好吧,只写。
使用 State,您可以访问之前存储的数据,并且可以在当前计算中使用这些数据:
def myComputation(x: A) =
State((myState: List[A]) => {
val newValue = calculateNewValueBasedOnState(x,myState)
(log |+| List(newValue), newValue)
})
使用 Writer,您可以将数据存储在您无权访问的某个对象中,您只能写入该对象。