'Is it possible to uniformly generate a random infinite bitstring lazily?

I tried to build an RNG that uniformly randomly chooses amongst 3 items. Because, the common idiom (something like C's rand() % 3) is prone to modulo bias, and thus not uniform.

As per the notion of admissibility, my idea was to uniformly generate a random infinite bitstring, and map it through a function. The following statements shall satisfy:

  • The function halts for almost all inputs (This "almost all" is a well-defined notion in measure theory)

  • The induced probability distribution over the 3 items is uniform

As such, my sketch of code was, in Haskell:

import Data.Word

import System.Random

infixr 5 :!

data InfWord64 = Word64 :! InfWord64

execute :: (InfWord64 -> a) -> IO a
execute f = do
    let getWordString = do
        headWord <- randomIO
        tailWords <- getWordString
        pure (headWord :! tailWords)
    fmap f getWordString

randomOrderingMap :: InfWord64 -> Ordering
randomOrderingMap (headWord :! tailWords)
  | headWord < 0x5555555555555555 = LT
  | 0x5555555555555555 < headWord && headWord < 0xAAAAAAAAAAAAAAAA = EQ
  | 0xAAAAAAAAAAAAAAAA < headWord = GT
  | otherwise = randomOrderingMap tailWords

randomOrdering :: IO Ordering
randomOrdering = execute randomOrderingMap

But it doesn't work properly. It seems execute would fall into an infinite loop for every input. It seems the monadic statement headWord <- randomIO would be executed infinitely.

I would need some kind of lazy IO, but it doesn't exist for good reasons. Lazy ST RealWorld would be an alternative, but I don't see any way to use this when the random package supports only strict ST. So what's the workaround?



Solution 1:[1]

The preferred way of generating a lazy infinite pseudorandom stream using System.Random is to use randoms. For example:

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Word

import System.Random
main = do
  infwords :: [Word64] <- randoms <$> newStdGen
  print $ take 10 infwords

Here, infwords is a normal, lazy, infinite Haskell list of pseudorandom Word64s.

Solution 2:[2]

In your code, it seems you want to work with a lazy list (or stream) of random numbers via getWordString, and then consume it in the pure function randomOrderingMap. Not an unreasonable approach to play around with, but creating such a lazy list, and actually deferring the execution of the next call to getWordString, requires lazy IO.

So if you replace the recursive call to getWordString with unsafeInterleaveIO getWordString (see docs), then it should work.

Try adding a putStrLn statement to getWordString to observe when exactly the IO action in this function is executed.

This is not necessarily the most direct or idiomatic way of solving this problem, and usually one does not reach for unsafe function, but as a learning experience this is useful.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 K. A. Buhr
Solution 2 Joachim Breitner