Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I think that doing your computations that require random numbers inside a monad that abstracts away the generator is the cleanest thing. Here is what that code would look like:</p> <p>We are going to put the StdGen instance in a state monad, then provide some sugar over the state monad's get and set method to give us random numbers.</p> <p>First, load the modules:</p> <pre><code>import Control.Monad.State (State, evalState, get, put) import System.Random (StdGen, mkStdGen, random) import Control.Applicative ((&lt;$&gt;)) </code></pre> <p>(Normally I would probably not specify the imports, but this makes it easy to understand where each function is coming from.)</p> <p>Then we will define our "computations requiring random numbers" monad; in this case, an alias for <code>State StdGen</code> called <code>R</code>. (Because "Random" and "Rand" already mean something else.)</p> <pre><code>type R a = State StdGen a </code></pre> <p>The way R works is: you define a computation that requires a stream of random numbers (the monadic "action"), and then you "run it" with <code>runRandom</code>:</p> <pre><code>runRandom :: R a -&gt; Int -&gt; a runRandom action seed = evalState action $ mkStdGen seed </code></pre> <p>This takes an action and a seed, and returns the results of the action. Just like the usual <code>evalState</code>, <code>runReader</code>, etc. </p> <p>Now we just need sugar around the State monad. We use <code>get</code> to get the StdGen, and we use <code>put</code> to install a new state. This means, to get one random number, we would write:</p> <pre><code>rand :: R Double rand = do gen &lt;- get let (r, gen') = random gen put gen' return r </code></pre> <p>We get the current state of the random number generator, use it to get a new random number and a new generator, save the random number, install the new generator state, and return the random number.</p> <p>This is an "action" that can be run with runRandom, so let's try it:</p> <pre><code>ghci&gt; runRandom rand 42 0.11040701265689151 it :: Double </code></pre> <p>This is a pure function, so if you run it again with the same arguments, you will get the same result. The impurity stays inside the "action" that you pass to runRandom.</p> <p>Anyway, your function wants pairs of random numbers, so let's write an action to produce a <em>pair</em> of random numbers:</p> <pre><code>randPair :: R (Double, Double) randPair = do x &lt;- rand y &lt;- rand return (x,y) </code></pre> <p>Run this with runRandom, and you'll see two different numbers in the pair, as you'd expect. But notice that you didn't have to supply "rand" with an argument; that's because functions are pure, but "rand" is an action, which need not be pure.</p> <p>Now we can implement your <code>normals</code> function. <code>boxMuller</code> is as you defined it above, I just added a type signature so I can understand what's going on without having to read the whole function:</p> <pre><code>boxMuller :: Double -&gt; Double -&gt; (Double, Double) -&gt; Double boxMuller mu sigma (r1,r2) = mu + sigma * sqrt (-2 * log r1) * cos (2 * pi * r2) </code></pre> <p>With all the helper functions/actions implemented, we can finally write <code>normals</code>, an action of 0 arguments that returns a (lazily-generated) infinite list of normally-distributed Doubles:</p> <pre><code>normals :: R [Double] normals = mapM (\_ -&gt; boxMuller 0 1 &lt;$&gt; randPair) $ repeat () </code></pre> <p>You could also write this less concisely if you want:</p> <pre><code>oneNormal :: R Double oneNormal = do pair &lt;- randPair return $ boxMuller 0 1 pair normals :: R [Double] normals = mapM (\_ -&gt; oneNormal) $ repeat () </code></pre> <p><code>repeat ()</code> gives the monadic action an infinite stream of nothing to invoke the function with (and is what makes the result of normals infinitely long). I had initially written <code>[1..]</code>, but I rewrote it to remove the meaningless <code>1</code> from the program text. We are not operating on integers, we just want an infinite list.</p> <p>Anyway, that's it. To use this in a real program, you just do your work requiring random normals inside an R action:</p> <pre><code>someNormals :: Int -&gt; R [Double] someNormals x = liftM (take x) normals myAlgorithm :: R [Bool] myAlgorithm = do xs &lt;- someNormals 10 ys &lt;- someNormals 10 let xys = zip xs ys return $ uncurry (&lt;) &lt;$&gt; xys runRandom myAlgorithm 42 </code></pre> <p>The usual techniques for programming monadic actions apply; keep as little of your application in <code>R</code> as possible, and things won't be too messy.</p> <p>Oh, and on other thing: laziness can "leak" outside of the monad boundary cleanly. This means you can write:</p> <pre><code>take 10 $ runRandom normals 42 </code></pre> <p>and it will work.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload