Hi. I finally got a hold on how to use monads (don't know if I understand them...), but my code is never very elegant. I guess is from a lack of grip on how all those functions on Control.Monad
can really help. So I'd thought it would be nice to ask for tips on this in a particular piece of code using the state monad.
The goal of the code is to calculate many kinds of random walks, and it's something I'm trying to do before something more complicated. The problem is that I have two stateful computations at the same time, and I'd like to know how to compose them with elegance:
- The function that updates the random number generator is something of type
Seed -> (DeltaPosition, Seed)
- The function that updates the position of the random walker is something of type
DeltaPosition -> Position -> (Log, Position)
(whereLog
is just some way for me to report what is the current position of the random walker).
What I've done is this:
I have a function to compose this two stateful computations:
composing :: (g -> (b, g)) -> (b -> s -> (v,s)) -> (s,g) -> (v, (s, g))
composing generate update (st1, gen1) = let (rnd, gen2) = generate gen1
(val, st2) = update rnd st1
in (val, (st2, gen2))
and then I turn it into a function that compose states:
stateComposed :: State g b -> (b -> State s v) -> State (s,g) v
stateComposed rndmizer updater = let generate = runState rndmizer
update x = runState $ updater x
in State $ composing generate update
And then I have the simplest thing, for example, a random walker that will just sum a random number to its current position:
update :: Double -> State Double Double
update x = State (\y -> let z = x+y
in (z,z))
generate :: State StdGen Double
generate = State random
rolling1 = stateComposed generate update
and a function to do this repeatedly:
rollingN 1 = liftM (:[]) rolling1
rollingN n = liftM2 (:) rolling1 rollings
where rollings = rollingN (n-1)
And then, if I load this in ghci
and run:
*Main> evalState (rollingN 5) (0,mkStdGen 0)
[0.9872770354820595,0.9882724161698186,1.9620425108498993,2.0923229488759123,2.296045158010918]
I get what I want, which is a list of the positions occupied by the random walker. But... I feel there must be a more elegant way to do this. I have two questions:
Can I rewrite those functions in a more "monadic" way, using clever functions from
Control.Monad
?Is there a general pattern about combining states like this that can be used? Does this have something to do with monad transformers or something like that?