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
实例和 get
和 put
的定义。真正的定义更笼统:
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 并将其设置为新状态。
因此,您在纯上下文中拥有状态。希望这有帮助。
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
实例和 get
和 put
的定义。真正的定义更笼统:
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 并将其设置为新状态。
因此,您在纯上下文中拥有状态。希望这有帮助。