Haskell 哈斯克尔州立大学

Haskell 哈斯克尔州立大学,haskell,state-monad,Haskell,State Monad,State Monad的put函数是否更新了实际状态,还是只返回一个新状态和新值?我的问题是,在命令式设置中,状态单子可以像“全局变量”一样使用吗?put是否修改“全局变量” 我的理解是,它不会修改初始状态,但使用一元接口,我们可以通过计算传递新状态,保持初始状态“完好无损”。这是正确的吗?如果没有,请纠正我 州没有什么神奇的。您可以这样实现它: newtype State s a = State {runState :: s -> (a, s)} 也就是说,状态sa(我们将其视为使用类

State Monad的
put
函数是否更新了实际状态,还是只返回一个新状态和新值?我的问题是,在命令式设置中,状态单子可以像“全局变量”一样使用吗?
put
是否修改“全局变量”


我的理解是,它不会修改初始状态,但使用一元接口,我们可以通过计算传递新状态,保持初始状态“完好无损”。这是正确的吗?如果没有,请纠正我

州没有什么神奇的。您可以这样实现它:

newtype State s a = State {runState :: s -> (a, s)}
也就是说,
状态sa
(我们将其视为使用类型为
s
的状态生成类型为
a
的结果的计算)只是一个函数,它接受一个状态并返回一个结果和一个新状态。您应该尝试写出此定义的
Monad
实例和
get
put
的定义。真正的定义更为笼统:

type State s = StateT s Identity
newtype Identity a = Identity a
newtype StateT s m a = StateT {runStateT :: s -> m (a, s)}
这允许将状态添加到其他一元计算中。也可以将状态转换器定义为“操作单体”。阿普费莫斯在某个地方有一个关于这些问题的教程。

首先,国家本身并不是“全球”的;您可以运行状态monad的多个不同副本,每个副本都有自己的独立状态,并且它们不会相互干扰。(事实上,这可以说是重点。)如果你想让州成为整个程序的全局,你就必须把整个程序放在一个州的单子中


其次,调用
put
会更改对
get
的后续调用将返回的结果。这就是全部。它不会“改变”实际值本身。例如,如果调用
get
并将结果放入某个变量中,然后调用
put
,则变量不会更改。即使状态是字典或其他东西,如果您要添加一个新键并
put
,任何仍在查看字典旧副本的人仍将看到旧字典。这对国家单子来说并不特别;哈斯克尔就是这样工作的。

答案就在类型上

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)
这本质上设置了将传递给组合中下一个函数的状态,而下游函数将看到修改后的状态

事实上,状态monad是完全纯的(即,没有设置任何内容);只有传递到下游的内容才会发生变化。换句话说,State monad为您省去了用Haskell这样的纯语言显式携带状态的麻烦。换句话说,State monad只是提供了一个隐藏状态线程细节的接口(我认为这就是WikiBooks中所说的,或者说是向您学习Haskell)

下面显示了这一点。您有get,它将value字段设置为与state字段相同(注意,当我指的是设置时,我指的是输出,而不是变量)
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,并将其设置为新状态

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