# [Haskell-cafe] Help with IO and randomR

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)

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
```