状态Monad与`put`函数

时间:2015-06-16 15:51:16

标签: haskell state monads

查看State Monad的wiki,我正在尝试了解runStateput函数。

据我了解runState,它需要State的第一个参数,其中包含“Universe”,s和值a。它需要宇宙的第二个参数。最后,它返回(a, s),其中a是新值,s是新宇宙?

ghci> :t runState
runState :: State s a -> s -> (a, s)

示例:

ghci> let s = return "X" :: State Int String
ghci> runState s 100
("X",100)

但是,我不理解put结果:

ghci> runState (put 5) 1
((),5)

由于runState返回(a, s),为什么类型为a的{​​{1}}?

我对上述尝试的解释没有信心。请纠正我,并在()上回答我的问题。

3 个答案:

答案 0 :(得分:6)

putState monad一起使用时,它有type s -> State s ()

put将状态设置为其参数,就是这样。至于它的返回值:它本质上是一个虚拟值,因为返回没什么用处。

这在definition put s = state $ \ _ -> ((), s)中也很明显。

答案 1 :(得分:1)

真正理解State的最简单方法,恕我直言,只是为了研究代码并充分理解它,你可以从内存中实现它,就像我要做的那样:

import Control.Applicative

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

instance Functor (State s) where
    fmap f fa = State $ \s -> f (runState fa s)

instance Applicative (State s) where
    pure a = State $ \s -> (a, s)
    ff <*> fa = State $ \ s -> 
        let (f, s') = runState ff s
            (a, s'') = runState fa s'
        in (f a, s'')

instance Monad (State s) where
    return = pure
    ma >>= f = State $ \s -> 
        let (a, s') = runState ma s
            (b, s'') = runState (f a) s'
        in (b, s'')

get :: State s s
get = State $ \s -> (s, s)

put :: s -> State s ()
put s = State $ \_ -> ((), s)

modify :: (s -> s) -> State s ()
modify f = State $ \s -> ((), f s)
  

由于runState返回(a, s),为什么类型为()

这真的只是武断的。以上述为基准,我们也可以这样写modifygetput

-- | Replaces the state with the result of applying it to the supplied
-- function.  The result of the action is the original state.
modify :: (s -> s) -> State s s
modify f = State $ \s -> (s, f s)

-- `get` is just what you get when you `modify` with an identity function.
get :: State s s
get = modify (\s -> s)

-- This version of `put` differs from the canonical one because it returns 
-- the old value.
put :: s -> State s s
put s = modify (\_ -> s) 

在此版本中,modifyput具有与原始版本相同的效果,但是附加产生旧状态作为其结果。仅针对效果使用modifyput的客户通常不会注意到这种差异。

或者,modifyput的“返回旧状态”版本可以根据官方版本编写。例如:

swap :: s -> State s s
swap s = do
    orig <- get
    put s
    return orig

因此,大多数这些操作是可以相互确定的,哪些是“基本的”而哪些不是......

答案 2 :(得分:1)

runState函数在状态monad和初始状态中执行操作,并返回两者计算结果和最终状态。

runState的一个典型用例是将一个带有初始值的复杂动作交给它,然后返回最终结果和状态。在您的示例中,操作是一个简单的原语put

put操作采用新状态并为值生成()(发音为unit)。它与putStrLn具有类型IO ()的方式非常类似。它在monad中执行操作,但不会产生有用的值。

因此,如果初始状态为runState (put 5) 1,则1会被新状态5吹走。状态计算的结果是put ()的结果。

只是为了踢,让我们看一些更有趣的事情:

runState (puts 5 >> return "hello world!") undefined
  --> ("hello world!", 5)

这里我们有两个与>>粘在一起的动作(我把它读作&#34;然后&#34;,它是绑定>>=的有限形式,我们只是从结果中删除结果左手边)。第一个操作将状态更改为5,第二个操作不会触及状态,但会产生字符串&#34; hello world!&#34;然后它成为整个计算的价值。