用副作用包装 class
Wrapping a class with side-effects
在阅读了 Functional Programming in Scala 的第六章并试图理解 State monad 之后,我有一个关于包装副作用的问题 class。
假设我有一个 class 以某种方式自我修改。
class SideEffect(x:Int) {
var value = x
def modifyValue(newValue:Int):Unit = { value = newValue }
}
我的理解是,如果我们将其包装在如下所示的 State 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
我能看到的唯一解决方案是复制 class 并修改它,但随着 SideEffect 的增长,这似乎在计算上很昂贵。另外,如果我们想包装像 Java class 这样没有实现 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?如何包装一个有副作用的 class 而不必复制它,或者这是唯一的方法吗?
- 我在这里使用的 State monad 的实现来自本书,可能与 Scalaz 的实现不同
除了在某些包装器中隐藏变异外,您不能对可变对象做任何事情。因此,在测试中需要更多关注的程序范围将小得多。你的第一个样本就足够了。只有一瞬间。最好完全隐藏外部引用。相反 stateMonad.run(sideEffect)
使用类似 stateMonad.run(new SideEffect(20))
或
的东西
def initState: SideEffect = new SideEffect(20)
val (state, value) = stateMonad.run(initState)
在阅读了 Functional Programming in Scala 的第六章并试图理解 State monad 之后,我有一个关于包装副作用的问题 class。
假设我有一个 class 以某种方式自我修改。
class SideEffect(x:Int) {
var value = x
def modifyValue(newValue:Int):Unit = { value = newValue }
}
我的理解是,如果我们将其包装在如下所示的 State 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
我能看到的唯一解决方案是复制 class 并修改它,但随着 SideEffect 的增长,这似乎在计算上很昂贵。另外,如果我们想包装像 Java class 这样没有实现 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?如何包装一个有副作用的 class 而不必复制它,或者这是唯一的方法吗?
- 我在这里使用的 State monad 的实现来自本书,可能与 Scalaz 的实现不同
除了在某些包装器中隐藏变异外,您不能对可变对象做任何事情。因此,在测试中需要更多关注的程序范围将小得多。你的第一个样本就足够了。只有一瞬间。最好完全隐藏外部引用。相反 stateMonad.run(sideEffect)
使用类似 stateMonad.run(new SideEffect(20))
或
def initState: SideEffect = new SideEffect(20)
val (state, value) = stateMonad.run(initState)