2015-10-18 115 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的工作原理。