[Haskell-cafe] Help with IO and randomR

Tillmann Rendel rendel at rbg.informatik.tu-darmstadt.de
Mon Jul 16 08:50:22 EDT 2007


Niko Korhonen wrote:
> So, in short, how do I do this without getting into an infinite loop:
> 
> tpdfs :: (Int, Int) -> IO [Int]
> tpdfs (low, high) = do
>   first <- getStdRandom (randomR (low, high))
>   second <- getStdRandom (randomR (low, high))
>   let r = (first + second) `div` 2
>   rest <- tpdfs (low, high)                    -- (A)
>   return (r : rest)                            -- (B)

(A) will be executed before (B) because of the IO monad. But you want r 
to be returned before rest is computed. I would split tpdfs in two 
functions: a pure function converting a infinite list of random numbers 
to another infinite list of random numbers, and an IO-function creating 
the original infinite list of random numbers:

   tpdfs' :: [Int] -> [Int]
   tpdfs' (x:y:rest) = (x + y) `div` 2 : tpdfs' rest

   tpdfs :: (Int, Int) -> IO [Int]
   tpdfs range = do
     gen <- newStdGen
     return (tpdfs' (randomRs range gen))

The only IO action (newStdGen) is executed when tpdfs is called, but the 
infinite result list is lazily created when needed. This is possible 
because newStdGen uses split to create a new source of randomness 
exclusively for the tpdfs' function wich is not accessed anywhere else.

tpdfs can be written more concisely as one of these

   tpdfs range = liftM (tpdfs' . randomRs range) newStdGen
   tpdfs range = return (tpdfs' . randomRs range) `ap` newStdGen
   tpdfs range = newStdGen >>= (randomRs range >>> tpdfs' >>> return)

using either Control.Monad or Control.Arrow.


I'm not sure your aproach is numerically correct. Let's assume range = 
(0, 1). The resulting number could be

   (0 + 0) `div` 2 = 0
   (0 + 1) `div` 2 = 0
   (1 + 0) `div` 2 = 0
   (1 + 1) `div` 2 = 1

with equal probability. Is this what you want?

   Tillmann Rendel


More information about the Haskell-Cafe mailing list