我有这个片段,你能解释一下这是做什么的,尤其是>> =?
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'
最终你能以更清晰的形式写出来吗? 感谢
答案 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
用于处理与状态传播相关的样板,即使函数不依赖于它 - 因为大多数函数都没有。当他们使用get
和put
访问元组的第二个投影时,他们开始依赖于状态并改变状态(好吧,它们实际上变为有状态)。没有State
单子会看起来像这样:
get :: (a,s) -> (s,s)
get (_,s) = (s,s)
put :: (s,s) -> ((),s)
put (s,_) = ((),s)
通过讨论(a,s)->(b,s)
,我们得到一个卫生的有状态计算,将对a
和s
的依赖关注分开:我们摆脱对s
的明确依赖,除非特别指出通过使用get
和put
来表达。记住他们的存在是很重要的 - 否则通过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)