And, to go further, once you embrace "determinism" in your randomness, you can do all sorts of really cool things.
From the perspective of a games programmer:
You can run the same simulation code on two different network nodes, and reliably get the same result, allowing you to just transfer user inputs between the nodes instead of game state. This has applications in reducing latency as well, as you only need to transfer the input one way across the network.
You can save off the user inputs and initial into a tiny "replay" buffer, allowing you to re-run the game from the beginning without much memory cost. This is not only a cool end-user feature, but it aids *tremendously* in debugging. When something goes wrong, you can always just rewind as many times as you want while you narrow down the cause of the problem.
However, we always had problems with determinism failures, where somebody would use the wrong random-number generator, or forget that they aren't allowed to have the simulation depend on something that came from the graphics RNG. In Haskell you can encode the purity of the simulation into its type and it won't break!
-- ryan
On Sun, 2009-10-04 at 05:11 -0700, Michael Mossey wrote:
> Duncan Coutts wrote:
> > Others have already answered but I'd like to suggest that you avoid
> > using IO here. There's no need for this to be impure.
> Can you point me to a tutorial that covers the basics of randomness inhttp://en.wikibooks.org/wiki/Haskell/Hierarchical_libraries/Randoms
> Hasell? I find it very confusing.
http://learnyouahaskell.com/input-and-output#randomness
The main thing to realise is that random number generators are pure and
predictable. Given the state of a random number generator, if you ask
for a random number, it always gives the same answer. It has to, because
it is pure.
Let's make one, and seed it with the starting state 12345
ghci> :module System.Random
ghci> let g0 = mkStdGen 12345
Now we can ask for the next random number in the sequence:
ghci> let (n1, g1) = next g0
ghci> n1
493972152
Now of course if we asked for the random number from g0 again then we
must get the same result. But notice that when we use 'next' it also
gives us back g1 which is the next state of the random number generator.
ghci> let (n2, g2) = next g1
ghci> n2
335387100
So this is the basic way that random number generators work in a pure
language. The generator has to be passed around the pure function, for
example from one recursion to the next.
So you end up with pure functions like:
shuffle :: RandomGen g => g -> [x] -> [x]
Another approach is to hide the 'g' inside a monad. That's what
MonadRandom is all about. eg:
shuffle :: [x] -> Rand [x]
The tutorials above explain about the other random functions, for
getting values of different types (not just Int) and restricted ranges
of number etc.
Of course at some point you want to seed the random number generator
with some initial genuinely random value (not like the 12345 we used
above). That is the only place in your random-handling code that needs
to do IO. All the rest of it can be pure.
Duncan
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe