This is an example from https://wiki.haskell.org/All_About_Monads
It's an example of using State
monad to thread the StdGen
value through a sequence of random numbers generating commands.
If I understand what the last return
does correctly it should just create a new monad with x
as a value. But then what does the put g'
actually do? Why wouldn't the g'
actually be lost?
getAny :: (Random a) => State StdGen a
getAny = do g <- get
(x,g') <- return $ random g
put g'
return x
I think you are confused by
(x, g') <- return $ random g
This indeed create a new monadic action State StdGen (a, StdGen)
, which is executed to extract its result (a, StdGen)
.
The confusion arises with good reason, because the code is actually equivalent to
let (x, g') = random g
where no monadic action is built, leading to more straightforward code.
This transformation is correct in any monad, not just the State
one.
Anyway, the technical part: the (x, g') <- return $ random g
snippet means
(x, g') <- State (\g'' -> (random g, g''))
where we can see that the monadic action takes the current state g''
(which has the same value as g), and then does not modify it (the (..., g'')
part) while returning the generated value random g
alongside it ((random g, ...)
part).
This is a bit silly, since we do not even need to read g''
since we are using random g
!
So, we are using
do g <- State (\g'' -> (g'', g''))
(x, g') <- State (\g'' -> (random g, g''))
...
when we could instead use
do (x, g') <- State (\g'' -> (random g'', g''))
...
which is called in the library
do (x, g') <- gets random
...
Okay, the confusion appears to be in do put g' ; return x
.
This is desugared into bind-notation as follows
{ definitions }
put g' = State $ \s -> ((), g')
return x = State $ \s -> (x , s )
do put g ; return x
= { definitions, desugaring }
(State $ \s -> ((), g'))
>>=
(\_ -> State $ \s -> (x , s ))
= { definition of >>= }
State $ \s -> let (v,s') = (\s -> ((), g')) s
in runState ((\_ -> State $ \s -> (x , s )) v) s'
= { beta-reduction (application) }
State $ \s -> let (v,s') = ((), g')
in runState (State $ \s -> (x , s )) s'
= { beta-reduction (let) }
State $ \s -> runState (State $ \s -> (x , s )) g'
= { runState (State y) = y }
State $ \s -> (\s -> (x , s )) g'
= { beta-reduction }
State $ \s -> (x , g')
So, the effect of do put g' ; return x
is to modify the state to g'
(overwriting the previous one s
) and to yield x
as a final value of the computation (alongside g'
).