这里有一个很棒的教程(链接),它似乎表明Writer Monad基本上是一个特殊情况的元组对象,代表(A,B)。Writer在左侧(即A)累积值,并且A有相应的Monoid(因此可以累积或修改状态)。如果A是一个集合,则它会进行累积。
State Monad也处理内部元组的对象。它们都可以进行flatMap、map等操作。对我来说,操作似乎是相同的。它们有什么不同之处?(请用Scala示例回复,我不熟悉Haskell)。谢谢!
这里有一个很棒的教程(链接),它似乎表明Writer Monad基本上是一个特殊情况的元组对象,代表(A,B)。Writer在左侧(即A)累积值,并且A有相应的Monoid(因此可以累积或修改状态)。如果A是一个集合,则它会进行累积。
State Monad也处理内部元组的对象。它们都可以进行flatMap、map等操作。对我来说,操作似乎是相同的。它们有什么不同之处?(请用Scala示例回复,我不熟悉Haskell)。谢谢!
Writer
更加受限,因为它不允许您读取积累的状态(直到您在最后一刻才能查看)。在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
将会在那里等待你。
def myComputation(x: A) =
State((myState: List[A]) => {
val newValue = calculateNewValueBasedOnState(x,myState)
(log |+| List(newValue), newValue)
})