This is very much a newbie question, and although I can find partial answers, I'm still having difficulty getting the whole thing to work. I have a collection of functions in a module with work with a particular data type I've designed. And sometimes I need to create a list of random values. We might consider my types as polynomials, given as lists of their coefficients. So I should just need to call a randomPoly
function (say) to generate new lists each time it's called. For simplicity all lists can be the same length (say 4) and with the same sized elements - say between 0 and 9.
So what I want is to be able to do something like this (in ghci):
>>> setRandomSeed 42
>>> randomPoly
[6,5,0,4]
>>> randomPoly
[9,6,3,5]
>>> randomPoly
[7,3,9,2]
I can of course obtain different random lists by simply passing a new seed to the generator each time I need a new list:
>>> randomPoly st = map (`mod` 10) $ take 4 $ randoms (mkStdGen st) :: [Integer]
But I don't want to do this: I want to set an initial seed once, and let State take care of managing the current value of the generator from then on.
At this stage I'm not much interested in the nitty-gritty of monads, State, and all the rest - I'm looking for as close to an "off the shelf" solution as possible. I just want something I can use. Most examples seem to be very keen to teach all about how the State monad works - and this is a very honorable notion - but right now all I want is some quick and simple method of creating random lists when I want them.
For example here's a silly function which creates random lists until a sum of all values is even:
mkEvenPoly :: Int -> [Integer]
mkEvenPoly st
| even $ sum p = p
| otherwise = mkEvenPoly $ s+1
where
p = map (`mod` 10) $ take 4 $ randoms (mkStdGen st) :: [Integer]
Note that each time I need to pass a new generating value to mkStdGen
. Can I do this sort of thing with the State monad - storing the current value of the generator (which is returned by mkStdGen
) and then using it for the next random call?
Here's an "off the shelf" solution that uses MonadRandom
and comes pretty close to what (I think) you have in mind.
I rewrote your randomPoly
and mkEvenPoly
functions to return a Rand
monad. As such, they can be composed in a do statement into a composite Rand
monad . Inside the do loop that generates this composite monad, random variables are set without the explicit use of a generator. A single generator is passed to runRand along with the composite monad to generate multiple random values.
import Control.Monad.Random
randomPoly :: Rand StdGen [Integer]
randomPoly = map (`mod` 10) <$> take 4 <$> getRandoms
mkEvenPoly :: Rand StdGen [Integer]
mkEvenPoly = do
p <- randomPoly
let res
| even $ sum p = return p
| otherwise = mkEvenPoly
res
-- Use do notation to compose multiple monads into one.
randomStuff :: Rand StdGen (Float, [[Integer]])
randomStuff = do
f <- getRandom
p0 <- randomPoly
p1 <- randomPoly
e0 <- mkEvenPoly
e1 <- mkEvenPoly
e2 <- mkEvenPoly
return (f, [p0, p1, e0, e1, e2])
main = do
-- set an initial seed once.
g0 <- getStdGen
-- actually generate the random values.
let (stuff, g1) = runRand randomStuff g0
print stuff