【问题标题】:Wrapping a class with side-effects用副作用包装一个类
【发布时间】:2017-05-30 06:21:30
【问题描述】:

在阅读了Functional Programming in Scala 的第六章并试图理解 State monad 之后,我有一个关于包装副作用类的问题。

假设我有一个以某种方式修改自身的类。

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

我能看到的唯一解决方案是复制该类并对其进行修改,但随着 SideEffect 的增长,这似乎计算成本很高。另外,如果我们想包装不实现 Cloneable 的 Java 类之类的东西,我们就很不走运了。

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 实现不同

【问题讨论】:

    标签: scala state-monad


    【解决方案1】:

    除了在某些包装器中隐藏突变之外,您不能对可变对象做任何事情。因此,在测试中需要更多关注的程序范围会小得多。你的第一个样品已经足够好了。只有一瞬间。完全隐藏外部参考会更好。取而代之的是 stateMonad.run(sideEffect) 使用 stateMonad.run(new SideEffect(20))

    def initState: SideEffect = new SideEffect(20)
    val (state, value) = stateMonad.run(initState)
    

    【讨论】:

      猜你喜欢
      • 2018-11-04
      • 1970-01-01
      • 1970-01-01
      • 2013-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-10
      相关资源
      最近更新 更多