[A supply of random numbers Bryan O'Sullivan **20080325061725] { addfile ./examples/ch16/RandomSupply.hs addfile ./examples/ch16/RandomGolf.hs addfile ./examples/ch16/randomSupply.ghci hunk ./en/ch16-monad-case.xml 422 + + Supplying random numbers + + If we want to use our Supply monad as a + source of random numbers, we have a small difficulty to face. + Ideally, we'd like to be able to provide it with an infinite + stream of random numbers. We can get a StdGen in + the IO monad, but we must put back + a different StdGen when we're done. If we don't, + the next piece of code to get a StdGen will get + the same state as we did. This means it will generate the + same random numbers as we did, which is potentially + catastrophic. + + From the parts of the System.Random module + we've seen so far, it's difficult to reconcile these demands. + We can use getStdRandom to ensure that + when we get a StdGen, we put one back. We can + use random to get back a new + StdGen when they give us a random number. And we + can use randoms to get an infinite list + of random numbers. But how do we get both an infinite list of + random numbers and a new + StdGen? + + The answer lies with the RandomGen type + class's split function, which takes one + random number generator, and turns it into two generators. + Splitting a random generator like this is a most unusual thing + to be able to do: it's obviously tremendously useful in a pure + functional setting, but essentially never either necessary or + provided by an impure language. + + Using the split function, we can use + one StdGen to generate an infinite list of random + numbers to feed to runSupply, while we + give the other back to the IO monad. + + &RandomSupply.hs:randomsIO; + + If we save the above definition in a file + RandomSupply.hs and load it into &ghci;, + we can try our randomsIO function out. + If we've written it properly, our example ought to print a + different random number on each invocation. + + &randomSupply.ghci:random; + + Recall that our runSupply function + returns both the result of executing the monadic action and + the unconsumed remainder of the list. Since we passed it an + infinite list of random numbers, we compose with + fst to ensure that we don't get drowned + in random numbers when &ghci; tries to print the + result. + + + + Another round of golf + + The pattern of applying a function to one element of a + pair, and constructing a new pair with the other original + element untouched, is common enough in Haskell code that it + has been turned into standard code. + + In the Control.Arrow module are two + functions, first and + second, that perform this + operation. + + &randomSupply.ghci:first; + + Now that we know about these functions, we can use one to + golf our definition of randomsIO, turning + it into a one-liner. + + &RandomGolf.hs:randomsIO_golfed; + + hunk ./examples/ch16/RandomGolf.hs 1 +import Supply +import System.Random +{-- snippet randomsIO_golfed --} +import Control.Arrow (first) + +randomsIO_golfed :: Random a => IO [a] +randomsIO_golfed = getStdRandom (first randoms . split) +{-- /snippet randomsIO_golfed --} hunk ./examples/ch16/RandomSupply.hs 1 +{-- snippet randomsIO --} +import Supply +import System.Random hiding (next) + +randomsIO :: Random a => IO [a] +randomsIO = + getStdRandom $ \g -> + let (a, b) = split g + in (randoms a, b) +{-- /snippet randomsIO --} hunk ./examples/ch16/randomSupply.ghci 1 +--# random +:load RandomSupply +(fst . runSupply next) `fmap` randomsIO +(fst . runSupply next) `fmap` randomsIO + +--# first +:m +Control.Arrow +first (+3) (1,2) +second odd ('a',1) }