Remember that Haskell functions are pure; they must always return the same result given the same input. You could make your function return IO [a]
instead, which would let you call newStdGen
, but a better way is to keep your code pure by taking the random number generator as an additional argument to your function and also returning the new generator afterwards:
rnd_select :: (Eq a, RandomGen g) => [a] -> Int -> g -> ([a], g)
rnd_select [] _ gen = ([], gen)
rnd_select _ 0 gen = ([], gen)
rnd_select ys n gen =
let (rnd_index, gen') = randomR (1, length ys) gen
(x, xs) = removeAt rnd_index ys
(xs', gen'') = rnd_select xs (n-1) gen'
in (x : xs', gen'')
Now you can use it with, e.g. getStdRandom :: (StdGen -> (a, StdGen)) -> IO a
like this.
> getStdRandom (rnd_select [1..20] 10)
[12,11,14,4,16,7,1,2,18,15]
Passing the generators around manually can be somewhat tedious, though. One way of making this neater is to use the MonadRandom package.
rnd_select :: (MonadRandom m, Eq a) => [a] -> Int -> m [a]
rnd_select [] _ = return []
rnd_select _ 0 = return []
rnd_select ys n = do
rnd_index <- getRandomR (1, length ys)
let (x, xs) = removeAt rnd_index ys
xs' <- rnd_select xs (n-1)
return (x:xs')
Since IO
is an instance of MonadRandom
, you can use this directly as an IO
action.
> rnd_select [1..20] 10
[20,18,12,13,5,7,17,9,3,4]
> rnd_select [1..20] 10
[9,18,4,20,6,5,3,15,13,7]
or you can use evalRand
to run this in a pure monad, providing your own random number generator so you can get repeatable results (good for debugging / testing).
> evalRand (rnd_select [1..20] 10) (mkStdGen 200)
[4,16,15,13,8,20,6,14,5,3]
> evalRand (rnd_select [1..20] 10) (mkStdGen 200)
[4,16,15,13,8,20,6,14,5,3]