有一个很棒的教程here似乎告诉我,Writer Monad基本上是一个特殊的案例元组对象,它代表(A,B)进行操作。作者在左边积累了值(即A),并且A与它有相应的Monoid(因此它可以累积或改变状态)。如果A是一个集合,那么它就会累积。
State Monad也是一个处理内部元组的对象。它们都可以是flatMap'd,map'd等等。这些操作对我来说都是一样的。他们有什么不同? (请用scala示例回复,我不熟悉Haskel)。谢谢!
答案 0 :(得分:35)
你对这两个单子密切相关的直觉是完全正确的。区别在于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
会等着您。
答案 1 :(得分:1)
tl; dr状态是读写,而Writer是,只有写。
使用State,您可以访问存储的先前数据,并且可以在当前计算中使用此数据:
def myComputation(x: A) =
State((myState: List[A]) => {
val newValue = calculateNewValueBasedOnState(x,myState)
(log |+| List(newValue), newValue)
})
使用Writer,您可以将数据存储在您无法访问的某个对象中,您只能写入该对象。