Haskell State Monad

问题描述 投票:3回答:3

State Monad的put函数是否更新实际状态,还是仅返回具有新值的新状态?我的问题是,在命令式设置中,可以将State Monad用作“全局变量”吗?并且put会修改“全局变量”吗?

我的理解是,不,它不会修改初始状态,但是使用monadic接口,我们可以传递新状态的黑白计算,而使初始状态保持“完整”。它是否正确?如果没有,请纠正我。

haskell state-monad
3个回答
4
投票

答案在于类型。

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

因此,状态本质上是一个函数,它接受一个参数's'(我们称为状态),并返回一个元组(值,状态)。 monad的实现如下所示

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)

这实质上是设置要传递给合成中下一个函数的状态,而下游函数将看到修改后的状态。

实际上,State monad是完全纯净的(也就是说,没有设置任何东西;只有传递给下游的内容会发生变化。换句话说,State monad省去了用Haskell这样的纯语言显式地建立州的麻烦。换句话说,State monad仅提供一个隐藏状态线程详细信息的接口(我认为这就是WikiBooks中的内容,或者我认为是Haskell的意思)。

下面显示了这一点。您已经得到get,它将值字段设置为与状态字段相同(请注意,当我指的是设置时,我指的是输出,而不是变量)。 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的value字段。现在,我们的看跌期权将获得该值,将其递增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)}

这允许将状态添加到其他单子计算中。也可以将状态转换器定义为“操作单子”。 Apfelmus在某个地方提供了有关它们的教程。


1
投票

首先,状态不是这样的“全局”;您可以运行状态monad的多个不同副本,每个副本都有其自己的独立状态,并且它们不会互相干扰。 (实际上,这可以说是重点。)如果您希望状态对于整个程序是全局的,则必须将整个程序置于单个状态monad中。

[其次,调用put会更改返回对get的后续调用的结果。就这样。它不会“更改”实际值本身。就像,如果您调用get并将结果放入某个变量中的某个位置,然后调用put,则您的变量不会更改。即使状态是字典或其他内容,如果您要添加一个新键并put,仍然在查看字典旧副本的任何人仍然会看到旧字典。这对州立monad并不特殊;这就是Haskell的工作方式。

© www.soinside.com 2019 - 2024. All rights reserved.