Random pain in Haskell
Random numbers seem to be a bit of a pain in Haskell. Because they are not a “pure” function (they involve hooking up with a system or user-supplied random number generator) there is a little scaffolding involved. Also when the number comes back, it's wrapped in an IO Ref, making the whole of your function impure — causing me at least a fair amount of grief.
I got stuck trying to do something more complicated and then went back to a simpler task: implementing a function pick
> pick :: [a] -> a
or rather
> pick :: [a] -> IO a
which will return an element from [a], chosen at random
The first, obvious implementation is
> pick a = do r <- getStdRandom (randomR (1, length a))
> return $ a !! (r-1)
The getStdRandom is the boilerplate I was mentioning (there may be better ways of doing this — I just cargo culted from the Haskell Report) while randomR gives a random integer in the range (min,max) provided.
This is (for once) rather longer than the equivalent Perl
sub pick { $_[rand( @_ )] }
It's also inefficient, as we have to scan all the elements of a to get the length and then another r elements to do the indexing.
There's a lovely algorithm to choose a random element of a sequence that only involves scanning it once. You start with your first element and select it. You move to the second element, and have a probability of (1/2) of selecting it. You move to the third, with a probability of (1/3)
Here's my code for it:
> pick :: [a] -> IO a
> pick [] = undefined
> pick [x] = do return x
> pick (x:xs) = pick' x xs 2
>
> pick' :: (Num p, Random p) => t -> [t] -> p -> IO t
> pick' curr [] _ = do return curr
> pick' curr (next:rest) prob
> = do r <- getStdRandom (randomR (1,prob))
> let curr' = if r == 1 then next else curr
> pick' curr' rest (prob+1)
Why in the auxhiliary function pick’ does p get typed as Random? As far as I can see, it's just a number, it increases monotonically and only participates in the function randomR as an upper bound.
Now this works in ghci:
*Main> pick [1,2,3,4,5]
3
Now if we want a list of random numbers:
*Main> map (\_ -> pick [1..5]) [1..5]
<interactive>:1:0:
No instance for (Show (IO t))
arising from use of `print' at <interactive>:1:0-32
Possible fix: add an instance declaration for (Show (IO t))
In the expression: print it
In a 'do' expression: print it
This is annoying. OK, maybe an IO value can't be showable because show returns a String, not an IO String, so using Show would allow you to subvert the IO sandbox. Or something. But if ghci can fake it in the case of a single IO value, why can't it do something sensible for a list of IO values?
Some interesting comments also on the algorithm to choose random numbers. Fair comment that my description of it is a little confusing - by "select", I mean "change the currently selected number for the new number". But the code is quite clear - evir and Sorear pointed out that Nethack (at least 3.4.3) has a citation of the algorithm and a proof in the comments! (in eat.c Elliott Kleinrock, October 5, 1990)

Comments
I think you're looking for mapM :: (a -
pick a = do r <- randomRIO (1, length a)
return $ a !! (r - 1)
Or shorter (but probably less clear):
pick a = randomRIO (0, length a - 1) >>= return . (a !!)
This is because you can't really show an IO action, (what ghci basicly does is execute any IO actions then print their results) what you can do though is turn a list of IO actions and turn them into an single IO action that produces a list. using the sequence function:
*Main> sequence $ map (\_ -> pick [1..5]) [1..5]
[3,4,1,4,4]
Theres even a shortcut for sequencing the results of a map:
*Main> mapM (\_ -> pick [1..5]) [1..5]
[1,2,2,1,1]
Also you might like to read my blog post on a random monad at http://paulspontifications.blogspot.com/2007/08/anatomy-of-new-monad.html
Paul.
You have a 50% chance to choose the first element. 33% to choose the second, 25% to choose the third, and so on, 1/(p+1) to choose.
You need a fair, equal chance to choose any number.
If you're truly concerned about having to run the list 'twice' (which is actually one and a half times in average), you can do this:
call the random generation function recursively, with a depth argument. If the element is the last of the list, you have a 1/(depth+1) chance to choose it.
When you start to go back in the stack, check if the last call returned a number. If so, return it. Otherwise, 1/(depth+1) to choose it.
Hey, it's stupid, slow, needs a huge stack, and you need to choose an element to represent "nothing chosen," but at least it's fair and doesn't run the list twice...
Note that the algorithm is trivially fair for any list of length 1.
Now suppose it is fair for any list of length n, and consider an arbitrary list of length n+1. After we have visited the first n elements, the chance that we will have picked each of those elements is 1/n, because the algorithm is fair for lists of length n. There is now a 1/(n+1) chance we will throw away the current candidate result and pick the last element. So, the probability that we will return the last element is 1/(n+1), and the probability that we will pick any other element is the probability that we had already picked it, times the probability that we don't throw it away in favour of the last element, which is 1/n * n/(n+1) = 1/n+1.
So by induction, the algorithm is always fair.
module UnsafeRandom (pick) where
import System.Random
import Foreign
pick :: [a] -> a
pick rl = unsafePerformIO $ do
r <- case rl of
[] -> error "empty list"
[x] -> return x
z@(x:xs) -> pick' x xs $ length z
return r
where
pick' x [] _ = do return x
pick' x (y:ys) z = do
r <- (getStdRandom . randomR) (1,z)
let c' = if r == 1 then y else x
pick' c' ys (z+1)
The oneof function in Test.QuickCheck is like your pick. It is in the Gen monad. Call generate with stdGen to remove from Gen.
More detail under
Using QuickCheck to Generate Random Data on http://en.wikibooks.org/wiki/Haskell/Hierarchical_libraries/Randoms