swaffelay
swaffelay

Reputation: 323

picking a random element from a list

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

Answers (2)

lynn
lynn

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

typetetris
typetetris

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

Related Questions