Aqui está a definição da mônada de estado do capítulo 13 de Learn You a Haskell
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
Com base nessa definição, parece-me que o estado na tupla retornada por f a
será substituído por newState
mesmo que seja a versão mais recente. Para pegar um exemplo específico do capítulo 10 do Real World Haskell, onde isso claramente não é o que os autores pretendem (observe o uso de em vez de porque eles ainda não introduziram formalmente as mônadas)==>
>>=
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
Onde
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
Em parseByte
, não vejo como initState
não substitui newState
.
Dê uma outra olhada na definição:
A
g newState
chamada não define o estado final paranewState
. Ela passa onewState
para a segundag
operação como seu estado inicial . Seg
alterar o estado, o estado final da operação combinada será um novo estado sem nome retornado pelag
chamada.Pode ajudar renomear algumas variáveis e dar um nome temporário ao valor de retorno da
g
chamada. Ao fazer isso, você pode escrever isso como a implementação funcionalmente idêntica:Isso ajuda a explicar o que está acontecendo? No lado direito desta definição, a operação composta que é construída aplica a primeira operação
op1 :: s -> (a, s)
que pega o PRIMEIRO estado e produz um valor do tipoa
e um SEGUNDO estado. Então, o valor do tipoa
pode ser passado paraf
para calcular a segunda operaçãoop2 :: s -> (b, s)
. Então, esta operação é passada para o SEGUNDO estado e produz um valor do tipob
e um TERCEIRO estado. Então, o estado final será o TERCEIRO estado, depois de ter sido transformado por ambas as operações, em ordem.A ideia é a mesma para
==>
. OinitState
é passado para ochainedParser
, mas a maneira como ochainedParser
funciona é que ele aplica ofirstParser
para aqueleinitState
, obtendo umnewState
, e então ele passa issonewState
para osecondParser
, que quando ele é executado porrunParser
retornará um estado final sem nome. Então,initState
enewState
são o estado inicial e final respectivamente defirstParser
, enewState
e algum "estado sem nome" são o estado inicial e final respectivamente desecondParser
.