看着你学习哈斯克尔的定义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.
你能解释一下他们的类型和发生的事情吗?
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)
在这里,a并s最终被同一类型.
现在这一切都很好,除非我们想要获得两个新数字,我们遇到了麻烦.为此,我们必须以某种方式运行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位---这就是目的State和runState
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)