包含具有副作用的类

时间:2017-05-29 21:50:15

标签: scala state-monad

在阅读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错了吗?如何在不必复制它的情况下包装副作用类,或者这是唯一的方法吗?

  • 我在这里使用的State monad的实现来自本书,可能与Scalaz实现不同

1 个答案:

答案 0 :(得分:2)

除了在某个包装器中隐藏变异外,你不能对可变对象做任何事情。因此,在测试中需要更多关注的程序范围将更小。你的第一个样本足够好了。只有一刻。最好隐藏外部参考。相反,stateMonad.run(sideEffect)使用stateMonad.run(new SideEffect(20))

之类的内容
def initState: SideEffect = new SideEffect(20)
val (state, value) = stateMonad.run(initState)