Haskell 状态单子

Haskell State Monad

State Monad 的 put 函数是更新实际状态还是只是 return 具有新值的新状态?我的问题是,State Monad 可以在命令式设置中像 "global variable" 一样使用吗? put 是否修改了 "global variable"?

我的理解是不,它不会修改初始状态,但是使用 monadic 接口我们可以绕过新状态 b/w 计算,留下初始状态 "intact"。这个对吗?如果不是,请纠正我。

State 没有什么神奇之处。你可以这样实现它:

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

也就是说,State s a(我们将其视为使用类型 s 的状态生成类型 a 的结果的计算)只是一个函数接受一个状态和returns一个结果和一个新状态。您应该尝试为此定义写出 Monad 实例和 getput 的定义。真正的定义更笼统:

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

这允许将状态添加到其他单子计算中。也可以将状态转换器定义为 "operational monads"。 Apfelmus 在某处有关于这些的教程。

首先,国家并非如此 "global";你可以有多个状态 monad 运行 的不同副本,每个副本都有自己独立的状态,并且它们不会相互干扰。 (事实上​​,这可以说是重点。)如果您希望状态对整个程序是全局的,则必须将整个程序放入单个状态 monad 中。

其次,调用 put 会改变后续调用 get 的结果 return。就这样。它不 "change" 实际值本身。就像,如果您调用 get 并将结果放入某处的变量中,然后调用 put,您的变量将不会改变。即使状态是字典或其他东西,如果你要添加一个新键和 put 那,任何仍在查看旧字典副本的人仍然会看到旧字典。这对 state monad 来说并不特别;这就是 Haskell 的工作原理。

答案就在类型中。

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

因此,状态本质上是一个函数,它接受一个参数,'s'(我们称之为状态),returns一个元组(值,状态)。 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 中所说的,或者 Learn you a 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。现在,我们的 put 将获取此值,将其递增 10 并将其设置为新状态。

因此,您在纯上下文中拥有状态。希望这有帮助。