以下是《Learn You a Haskell》第 13 章中状态单子的定义
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
根据此定义,在我看来,虽然返回的是较新的版本,但tuple 中的状态f a
似乎将被 替换。以Real World Haskell第 10 章中的具体示例为例,这显然不是作者的本意(请注意使用而不是 ,因为他们尚未正式引入 monad)newState
==>
>>=
parseByte :: Parse Word8
parseByte =
getState ==> \initState ->
case L.uncons (string initState) of
Nothing ->
bail "no more input"
Just (byte,remainder) ->
putState newState ==> \_ ->
identity byte
where newState = initState { string = remainder,
offset = newOffset }
newOffset = offset initState + 1
在哪里
newtype Parse a = Parse {
runParse :: ParseState -> Either String (a, ParseState)
}
identity :: a -> Parse a
identity a = Parse (\s -> Right (a, s))
(==>) :: Parse a -> (a -> Parse b) -> Parse b
firstParser ==> secondParser = Parse chainedParser
where chainedParser initState =
case runParse firstParser initState of
Left errMessage ->
Left errMessage
Right (firstResult, newState) ->
runParse (secondParser firstResult) newState
在parseByte
,我不明白如何initState
不取代newState
。
再看一下定义:
该
g newState
调用不会将最终状态设置newState
为。它将 传递newState
给第二个g
操作作为其起始状态。如果g
更改了状态,则组合操作的最终状态将是该g
调用返回的新的未命名状态。重命名一些变量并为调用的返回值指定一个临时名称可能会有所帮助
g
。通过这样做,您可以将其写为功能相同的实现:这有助于解释发生了什么吗?在这个定义的右侧,构造的复合操作应用了第一个操作,
op1 :: s -> (a, s)
该操作采用FIRST状态并生成类型的值a
和SECONDa
状态。然后,可以将类型的值传递给f
计算第二个操作op2 :: s -> (b, s)
。然后,此操作传递SECOND状态并生成类型的值b
和THIRD状态。因此,在按顺序通过两个操作进行转换后,最终状态将是THIRD状态。其思想与 相同
==>
。initState
将 传递给chainedParser
,但 的工作方式chainedParser
是将 应用于firstParser
,initState
得到newState
,然后将 传递newState
给secondParser
,当 运行时runParser
将返回一个未命名的最终状态。因此,initState
和newState
分别是 的起始和结束状态firstParser
, 和newState
某个“未命名状态”分别是 的起始和结束状态secondParser
。