2017-05-29 53 views
0

在閱讀Functional Programming in Scala的第6章並試圖瞭解國家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增長計算昂貴。另外,如果我們想要包裝一些類似於不實現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是否錯誤?一個人如何去包裝一個副作用的課程,而不必複製它,或者這是唯一的方法?

  • 對我使用這裏的國家單子的實施是一本書,並可能從Scalaz實現不同

回答

2

你不能這樣做與可變對象任何東西,除了一些隱藏突變包裝。所以在測試中需要更多關注的程序範圍會小得多。你的第一個樣本足夠好。只有一個時刻。完全隱藏外部參照會更好。相反stateMonad.run(sideEffect)使用類似stateMonad.run(new SideEffect(20))

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