I want to apply a function f which needs a RandomGen over a list. I tried to generate me therefore a infinite list of RandomGen as you can see below. (just random values as generated by the function "randoms" isn't sufficient, because the needed range of the value depends on the input for f.)
module Test where
import System.Random (getStdGen, RandomGen, randomR, random)
f :: (RandomGen g, Integral a) => g -> a -> Bool
randomGens :: RandomGen g => g -> [g]
randomGens gen =
let (i, gen') = (random gen) :: (Int, g1)
in gen : (repeatGen gen')
Unfortunately the compiler tells me, that it fails
Test.hs:13:26:
Could not deduce (g1 ~ g)
from the context (RandomGen g)
bound by the type signature for
randomGens :: RandomGen g => g -> (g, g)
at Test.hs:11:14-39
or from (RandomGen g1)
bound by an expression type signature: RandomGen g1 => (Int, g1)
at Test.hs:13:19-55
`g1' is a rigid type variable bound by
an expression type signature: RandomGen g1 => (Int, g1)
at Test.hs:13:19
`g' is a rigid type variable bound by
the type signature for randomGens :: RandomGen g => g -> (g, g)
at Test.hs:11:14
In the first argument of `random', namely `gen'
In the expression: random gen :: RandomGen g => (Int, g)
In a pattern binding:
(i, gen') = random gen :: RandomGen g => (Int, g)
Just skipping in the let-binding the type annotation (Int, g1) doesn't work. He needs to have the result-type for the application of "random"
You can define the function you want as
randomGens :: (RandomGen g) => g -> [g]
randomGens g = let (g0,g1) = split g in g0 : randomGens g1
The above probably isn't the best way to go about applying a function that requires randomness to a list. I might define a helper function to do that
mapRandom :: (RandomGen g) => (g -> a -> b) -> g -> [a] -> (g, [b])
mapRandom _ g [] = (g, [])
mapRandom f g (a:as) = let (_,g1) = next g
in f g a : mapRandom f g1 as
You can then write
>> g <- newStdGen
>> mapRandom f g [1..5]
([False,False,True,False,True], 1839473859 293842934)
The function mapRandom
looks very messy. That's because we have to mess around with the fiddly details of manually updating the generator. Fortunately, you don't have to do that! The package Control.Monad.Random
gives you nice combinators to almost completely abstract away the idea of generators. Say you currently have
f :: (RandomGen g) => g -> Int -> Bool
f g n = let (x,_) = random g in x < n
I would rewrite that to be
f :: (RandomGen g) => Int -> Rand g Bool
f n = do
x <- getRandom
return (x < n)
and just use mapM
to map this function over lists. You can run it with
>> gen <- newStdGen
>> runRand (mapM f [1..10]) gen
([False,True,True,False,True], 1838593757 1838473759)
where the first element of the pair is the result of mapping your random function over the list, and the last element is the current value of the generator. Notice that when defining f
you don't have to worry about the generator at all - Haskell takes care of updating the generator and generating new random numbers behind the scenes.