Alguém pode me guiar por essa função Haskell (Related Monad relacionada)?
Pergunta
tick :: State Int Int
tick = get >>= \n ->
put (n+1) >>= \y ->
return n
Estou confuso sobre como put (n+1)
tem algum efeito no resultado final dessa função. Parece que essa função deve retornar o estado inicial inalterado. Estou tentando passar por isso em minha mente, mas continuo saindo fora de espaço para manter as coisas no lugar. :
Se alguém pudesse me levar pela avaliação dessa função, seria realmente útil.
Solução
... Como é a atualização do estado em primeiro lugar? Parece apenas estar sentado lá não fazendo nada ...
Ah, agora eu entendo sua pergunta. Você está se perguntando como put
(e get
) Trabalho, certo?
Talvez um exemplo no JavaScript ajude (um idioma com estado mutável real):
var s; // mutable state
function get() { return s; }
function put(x) { s = x; }
function tick() {
var n = get();
put(n + 1);
return n;
}
Espero que isso ilustre que, enquanto n
Não muda, o estado interno ainda será atualizado. Se você executar tick()
Duas vezes, o estado será incrementado duas vezes.
Para voltar a Haskell, aqui está a definição completa de (as partes relevantes) do State
Mônada:
newtype State s a = State { runState :: s -> (a, s) }
instance Monad (State s) where
return a = State $ \s -> (a, s)
m >>= k = State $ \s -> let
(a, r) = runState m s
in runState (k a) r
get = State $ \s -> (s, s)
put s = State $ \_ -> ((), s)
Agora tente expandir o seu tick
Exemplo ainda mais adiante manualmente >>=
, return
, get
e put
. Espero que fique mais claro como o estado funciona.
Outras dicas
Você está completamente certo. O resultado de tick
"Função" é o valor inicial do estado.
Agora, claro, tick
não é a verdadeira "função", mas um cálculo que pode ler e escrever Estado antes de produzir um resultado.
Neste caso, o Estado é atualizado, mas você ainda está retornando o valor original do estado:
-- 4 is the inital state
ghci> runState tick 4
(4, 5)
-- 4 is the result of the tick computation, 5 is the updated state
Nesse caso, já que você nunca mais está inspecionando o estado dentro tick
, você não está vendo o estado alterado. No entanto, se algum outro cálculo acontecer depois tick
, pode ver o estado atualizado.
Por exemplo, fazendo tick
duas vezes (o segundo lerá o estado atualizado):
-- 4 is the inital state
ghci> runState (tick >> tick) 4
(5, 6)
-- 5 is the result of the tick computation executed twice,
-- 6 is the updated state
Pode ajudar a escrever usando do
notação
tick :: State Int Int
tick = do
n <- get -- get the state
put (n+1) -- save an incremented state
return n -- return the original state
enquanto o put (n+1)
Não afeta o resultado do cálculo, ele altera o estado que é mantido dentro da Mônada do Estado.