Reputation: 323
So in a project of my I have a constant function that returns an array :
import System.Random
giveList :: [Int]
giveList = [8,9,4,5,2]
and I would like to randomly select an element from that list like this:
seed::Int
seed = 40
generator = mkStdGen seed
giveRandomElement :: Int
giveRandomElement = giveList !! rand where
n = length tetrominoes
(rand,generator) = randomR (0,(n-1)) generator
However, this does not compile because of the generator, I would like to keep the generator as a global variable so I don't have to keep giving it to functions. I also don't want to deal with IO wrappers, so in what way can I do this ?
Thx for the help :-)
Upvotes: 3
Views: 8226
Reputation: 10814
I would like to keep the generator as a global variable so I don't have to keep giving it to functions. I also don't want to deal with IO wrappers, so in what way can I do this ?
Sorry, a function giveRandomElement :: Int
that returns a different random number every time you call it isn't referentially transparent, so Haskell's purity does not allow you to do what you'd like.
I think working with random values is a great opportunity to learn about monads. MonadRandom has a very flexible interface for writing functions whose results are random:
die :: (MonadRandom m) => m Int
die = getRandomR (1, 6)
rollDice :: (MonadRandom m) => m (Int, Int)
rollDice = do
x <- die
y <- die
return (x, y)
-- Your example
giveRandomElement :: (MonadRandom m) => m Int
giveRandomElement = do
let n = length tetrominoes
i <- getRandomR (0, n-1)
return (giveList !! i)
The implementations of this monad will thread the randomness state through your random functions for you. There's even an instance MonadRandom IO
, which lets you write
main :: IO ()
main = do
x <- giveRandomElement
print x
Upvotes: 8
Reputation: 4867
A working code example
import System.Random
seed::Int
seed = 40
giveList :: [Int]
giveList = [8,9,4,5,2]
generator = mkStdGen seed
giveRandomElement :: Int
giveRandomElement = giveList !! rand where
n = length giveList
(rand, _) = randomR (0,(n-1)) generator
But that probably doesn't do, what you want.
giveRandomElement
will always yield the same result. It is a pure function without any inputs, so what should it do? It can only be constant.
You either need to use IO
or you need to thread your generator through your code and keep track of it somewhere.
The compiler error you got:
test.hs:14:23: error:
• Ambiguous type variable ‘g0’ arising from a use of ‘randomR’
prevents the constraint ‘(RandomGen g0)’ from being solved.
Relevant bindings include generator :: g0 (bound at test.hs:14:10)
Probable fix: use a type annotation to specify what ‘g0’ should be.
These potential instance exist:
instance RandomGen StdGen -- Defined in ‘System.Random’
• In the expression: randomR (0, (n - 1)) generator
In a pattern binding:
(rand, generator) = randomR (0, (n - 1)) generator
In an equation for ‘giveRandomElement’:
giveRandomElement
= giveList !! rand
where
n = length giveList
(rand, generator) = randomR (0, (n - 1)) generator
|
14 | (rand, generator) = randomR (0,(n-1)) generator
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
was because you defined the symbol generator
in giveRandomElement
in terms
of it self and so the compiler couldn't deduce its type. (The top level declaration generator
wasn't used in that case, because (rand, generator) =
already shadowed the symbol after the equal sign.
Upvotes: 11