2015-10-18 74 views
3

在狀態Monad(Haskell)中是否'放入',更新實際狀態還是僅僅返回一個新值的新狀態?我的問題是,國家單體在命令性環境中能否像一個「全局變量」一樣使用?並'放'修改「全局變量」?Haskell State Monad

我的理解是NO它不會修改initialState,但是使用monadic接口,我們可以繞過新的狀態b/w計算,使初始狀態「完好無損」。它是否正確?如果沒有,請糾正我。

謝謝。

回答

4

答案在類型中。

newtype State s a = State {runState :: s -> (a, s)} 

因此,狀態實質上是一個函數,它的一個參數,「S」(我們稱之爲狀態),並且返回一個元組(值,狀態)。單子被實現像下面

instance Monad (State s) where 
    return a = State $ \s -> (a,s) 
    (State f) >>= h = State $ \s -> let (a,s') = f s 
            in (runState h a) s' 

因此,您有操作上的初始狀態和吐出值狀態元組通過在組合物中的下一個函數進行處理的功能。

現在,put是以下功能。

put newState = State $ \s -> ((),newState) 

這基本上設置一個將被傳遞到在組合物中的下一個功能和下游功能將看到修改狀態的狀態。事實上,國家monad是完全純粹的(即沒有設置任何東西);只有通過下游的變化。換句話說,State monad爲你省去了像Haskell這樣純粹的語言顯式地攜帶狀態的麻煩。換句話說,State monad只提供了一個隱藏狀態線程細節的接口(這就是WikiBooks和/或學習你所學的Haskell,我認爲)。

下面顯示了這一行動。你已經得到了,它將值字段設置爲與狀態字段相同(請注意,當我意指設置時,我的意思是輸出,而不是變量)。 put通過傳遞給它的值獲取狀態,增加它並用此新值設置狀態。

-- execState :: State s a -> s -> s 
let x = get >>= \x -> put (x+10) 
execState x 10 

以上輸出20

現在,讓我們做到以下幾點。

execState (x >> x) 10 

這會給30.第一x的輸出通過PUT設置狀態爲20。這現在被第二個x使用。 get在這一點上設置狀態將它傳遞給現在爲20的值字段。現在,我們的put將得到這個值,將它增加10並將其設置爲新狀態。

因此,您在純上下文中具有狀態。希望這可以幫助。

5

State沒什麼神奇的。您可以實現這樣的:

newtype State s a = State {runState :: s -> (a, s)} 

也就是說,考慮到一國一State s a(我們認爲的使用s類型的狀態產生a類型的結果計算)僅僅是一個函數並返回一個結果和一個新的狀態。對於此定義,您應該嘗試寫出Monad實例和getput的定義。真正的定義是更一般的:

type State s = StateT s Identity 
newtype Identity a = Identity a 
newtype StateT s m a = StateT {runStateT :: s -> m (a, s)} 

這允許狀態被添加到其他monadic計算。也可以將狀態變換器定義爲「操作單子」。 Apfelmus在某個地方有一個教程。

1

首先,國家不是「全球化」的;你可以運行狀態monad的幾個不同副本,每個副本都有自己獨立的狀態,並且它們不會互相干擾。 (的確,這可以說是整個觀點。)如果你想讓這個狀態成爲整個程序的全局,那麼你必須把整個程序放到一個狀態monad中。

其次,調用put更改後續調用get將返回的結果。就這樣。它不會「改變」實際價值本身。比如,如果您致電get並將結果放入某個變量中,然後致電put,則變量不會更改。即使國家是字典或其他東西,如果你要添加一個新的密鑰和put,任何人仍然在看字典的舊副本仍然會看到舊的字典。這對國家monad來說並不特別;這正是Haskell的工作原理。