Haskell State monad解释

时间:2014-12-03 12:34:26

标签: haskell

我有这个片段,你能解释一下这是做什么的,尤其是>> =?

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

最终你能以更清晰的形式写出来吗? 感谢

3 个答案:

答案 0 :(得分:6)

此代码定义 >>=应该做什么。

State func1 >>= f    =
  State $ ...stuff...

State构造函数后面必须跟一个获取当前状态并返回新状态和结果的函数。因此...stuff...必须是这样的功能,func1也必须如此。

State $ \ state1 -> ...

好的,我们希望通过给它一些状态来“运行”func1

let (resultA, state2) = func1 state1

接下来,我们要调用f来执行下一个monadic操作:

State func2 = f resultA

现在我们“运行”它,将其state2而不是state1

in func2 state2

上面的示例代码使用runState,但为了清晰起见,我认为我会明确拼写出来。

State func1 >>= f   =
  State $ state1 ->
    let (resultA, state2) = func1 state1
        State func2 = f resultA
    in  func2 state2

答案 1 :(得分:5)

如果你眯着眼睛,你会发现State确实是与(a,s)->(b,s)类型的函数相关的功能的包装器,只有curry:a -> s -> (b,s)

类型(a,s)->(b,s)的链接函数非常简单 - 简单的组合。这样他们就可以通过" state" (s)和计算"结果" (a沿调用链生成b)。当然,显而易见的是" state"和"结果"是任意的。但如果你正在处理咖喱版本,它会变得有点复杂,并由>>=处理。

但是Monad是什么,如果我们可以"链接真的很容易"在currying之前的类型的功能? Monad用于处理与状态传播相关的样板,即使函数不依赖于它 - 因为大多数函数都没有。当他们使用getput访问元组的第二个投影时,他们开始依赖于状态并改变状态(好吧,它们实际上变为有状态)。没有State单子会看起来像这样:

get :: (a,s) -> (s,s)
get (_,s) = (s,s)
put :: (s,s) -> ((),s)
put (s,_) = ((),s)

通过讨论(a,s)->(b,s),我们得到一个卫生的有状态计算,将对as的依赖关注分开:我们摆脱对s的明确依赖,除非特别指出通过使用getput来表达。记住他们的存在是很重要的 - 否则通过s传播的问题并不完全明显:

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

现在,回到>>=。这个函数的作用是将s->(a,s)a->s->(b,s)结合起来获得s->(b,s) - 现在可以用来与>>=结合使用。如果我们没有State构造函数包装器,绑定将如下所示:

as >>= f = \s -> (uncurry f) $ as s

也就是说,给定状态,请输入as以生成(a,s),这些f被输入(a,s)->(b,s)(未经证实再次获得(b,s))以生成State }。但是因为我们有(State as) >>= f = State $ \s -> let (a,s') = as s --mean to call f (a,s'), but (State g) = f a --need to remove the wrapper in g s' --to pass s' to (s->(b,s)) 包装器,所以它变得不那么明显了:

runState

您的示例中的展开是使用{{1}}完成的。

答案 2 :(得分:4)

想象一下有状态的功能:

type StatefulFunction s a b = s -> a -> (b, s)

让我们创建两个这样的功能:

a :: StatefulFunction Int Int Int
a s x = (x + s, s)

b :: StatefulFunction Int Int Int
b s x = (x + s, s+1)

这些函数可以根据显式状态参数更改行为。然而,链接它们是乏味的:

let startState = 0

let aParam = 42
let (aResult, newState) = a startState x

let bParam = 99
let (bResult, newState') = b newState y

State monad使这个链更容易,我上面写的正是>>=正在做的事情:

a :: Int -> State Int Int
a x = fmap (+x) get

b :: Int -> State Int Int
b x = do 
    s <- get
    put $ s + 1
    return $ s + x

foo :: State Int Int
foo = (a aParam) >>= (b bParam)