¿Alguien puede caminar a través de esta función Haskell (estado relacionado mónada)?
Pregunta
tick :: State Int Int
tick = get >>= \n ->
put (n+1) >>= \y ->
return n
Estoy confundido en cuanto a cómo put (n+1)
tiene ningún efecto sobre el resultado final de esta función en absoluto. Parece que esta función debe volver al estado inicial sin cambios. Estoy tratando de ejecutar este en mi mente, pero sigo quedando sin espacio para las cosas se mantienen en su lugar. : \
Si alguien pudiera caminar a través de la evaluación de esta función, sería de gran ayuda.
Solución
... ¿Cómo se pone actualizando el estado en el primer lugar? Parece que acaba de estar sentado sin hacer nada ...
Ah, ahora entiendo su pregunta. Usted se pregunta cómo put
(y get
) de trabajo, ¿verdad?
Tal vez un ejemplo en JavaScript ayudará (un lenguaje con el estado mutable 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 esto ilustra que, mientras n
no cambia, el estado interno todavía se actualizará. Si se ejecuta tick()
dos veces, el estado se incrementará en dos ocasiones.
Para volver a Haskell, aquí está la definición completa de (las partes pertinentes) de la mónada State
:
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)
Ahora trata de ampliar su ejemplo tick
aún más mediante la inclusión entre líneas manualmente >>=
, return
, get
y put
. Es de esperar que se pondrá más claro cómo funciona Estado.
Otros consejos
Usted es completamente correcto. El "número" de "función" tick
es el valor inicial del estado.
Ahora, por supuesto, tick
no es la "función" real, sino un cálculo que puede leer y escribir Estado antes de producir un resultado.
En este caso, el Estado se actualiza, pero todavía se está volviendo el valor original del 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
En este caso, ya que nunca se está inspeccionando el estado de nuevo en el interior tick
, no estás viendo el cambio de estado. Sin embargo, si algún otro tipo de cálculo que sucede después de tick
, se puede ver el estado actualizado.
Por ejemplo, haciendo tick
dos veces (la segunda leerá el estado actualizado):
-- 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
que podría ayudar a escribir usando la notación do
tick :: State Int Int
tick = do
n <- get -- get the state
put (n+1) -- save an incremented state
return n -- return the original state
mientras que el put (n+1)
no influye en el resultado del cálculo, sí altera el estado en que se lleva a cabo dentro de la mónada estado.