了解国家Monad

Kev*_*ith 4 haskell

看着你学习哈斯克尔的定义State Monad:

instance Monad (State s) where  
    return x = State $ \s -> (x,s)  
    (State h) >>= f = State $ \s -> let (a, newState) = h s  
                                        (State g) = f a  
                                    in  g newState  
Run Code Online (Sandbox Code Playgroud)

我不明白右下角的类型h s和类型g newState.

你能解释一下他们的类型和发生的事情吗?

J. *_*son 7

State s a 是函数的命名---"状态变换函数"

s -> (a, s)
Run Code Online (Sandbox Code Playgroud)

换句话说,它采用输入状态s并在返回结果的同时修改该状态a.这形成了一个真正的"纯国家"框架.如果我们的状态是一个整数,我们可以编写一个更新该整数并返回新值的函数---这就像一个唯一的数字源.

upd :: Int -> (Int, Int)
upd s = let s' = s + 1 in (s', s')
Run Code Online (Sandbox Code Playgroud)

在这里,as最终被同一类型.


现在这一切都很好,除非我们想要获得两个新数字,我们遇到了麻烦.为此,我们必须以某种方式运行upd两次.

最终的结果是另一个状态变压器功能,所以我们正在寻找一个"状态变压器变压器".我会称它为:

compose :: (s -> (a, s))         -- the initial state transformer
        -> (a -> (s -> (b, s)))  -- a new state transformer, built using the "result"
                                 -- of the previous one
        -> (s -> (b, s))         -- the result state transformer
Run Code Online (Sandbox Code Playgroud)

这看起来有点毛茸茸,但说实话,写这个功能相当容易.这些类型指导您回答:

compose f f' = \s -> let (a, s')  = f s
                         (b, s'') = f' a s'
                     in  (b, s'')
Run Code Online (Sandbox Code Playgroud)

您会注意到s-typed变量[s, s', s'']"向下流动",表示状态从第一次计算移动到第二次计算结果.

我们可以compose用来构建一个使用两个唯一数字的函数upd

twoUnique :: Int -> ((Int, Int), Int)
twoUnique = compose upd (\a s -> let (a', s') = upd s in ((a, a'), s'))
Run Code Online (Sandbox Code Playgroud)

这些是基础知识State.唯一的区别是我们认识到compose函数内部存在一个共同的模式,我们将其提取出来.这种模式看起来像

(>>=) :: State s a     -> (a -> State s b   ) -> State s b
(>>=) :: (s -> (a, s)) -> (a -> (s -> (b, s)) -> (s -> (b, s))
Run Code Online (Sandbox Code Playgroud)

它也以同样的方式实现.我们只需要"包装"和"解包"的State位---这就是目的StaterunState

State    :: (s -> (a, s)) -> State s a
runState :: State s a     -> (s -> (a, s))
Run Code Online (Sandbox Code Playgroud)

现在我们可以把compose它与之比较(>>=)

compose f f'       =         \s -> let (a, s')  = f s
                                       (b, s'') =           f' a  s'
                                   in  (b, s'')

(>>=) (State f) f' = State $ \s -> let (a, s')  = f s
                                       (b, s'') = runState (f' a) s'
                                   in  (b, s'')
Run Code Online (Sandbox Code Playgroud)