在阅读Functional Programming in Scala的第六章并试图了解State monad之后,我有一个关于包装副作用类的问题。
假设我有一个课程可以修改自己。
class SideEffect(x:Int) {
var value = x
def modifyValue(newValue:Int):Unit = { value = newValue }
}
我的理解是,如果我们把它包装在一个状态monad中,如下所示,它仍然会修改原件,使得包装它没有实际意义。
case class State[S,+A](run: S => (A, S)) { // See footnote
// map, flatmap, unit, utility functions
}
val sideEffect = new SideEffect(20)
println(sideEffect.value) // Prints "20"
val stateMonad = State[SideEffect,Int](state => {
state.modifyValue(10)
(state.value,state)
})
stateMonad.run(sideEffect) // run the modification
println(sideEffect.value) // Prints "10" i.e. we have modified the original state
我能看到的唯一解决方案就是制作一个类的副本并对其进行修改,但随着SideEffect的增长,这看起来似乎很昂贵。另外,如果我们想要包装像Java类那样没有实现Cloneable的东西,那我们就不走运了。
val stateMonad = State[SideEffect,Int](state => {
val newState = SideEffect(state.value) // Easier if it was a case class but hypothetically if one was, say, working with a Java library, one would not have this luxury
newState.modifyValue(10)
(newState.value,newState)
})
stateMonad.run(sideEffect) // run the modification
println(sideEffect.value) // Prints "20", original state not modified
我使用State monad错了吗?如何在不必复制它的情况下包装副作用类,或者这是唯一的方法吗?
答案 0 :(得分:2)
除了在某个包装器中隐藏变异外,你不能对可变对象做任何事情。因此,在测试中需要更多关注的程序范围将更小。你的第一个样本足够好了。只有一刻。最好隐藏外部参考。相反,stateMonad.run(sideEffect)
使用stateMonad.run(new SideEffect(20))
或
def initState: SideEffect = new SideEffect(20)
val (state, value) = stateMonad.run(initState)