haskellunsafe-perform-io

(Edited) How to get random number in Haskell without IO


I want to have a function that return different stdGen in each call without IO. I've tried to use unsafePerformIO, as the following code.

import System.IO.Unsafe
import System.Random

myStdGen :: StdGen
myStdGen = unsafePerformIO getStdGen

But when I try to call myStdGen in ghci, I always get the same value. Have I abused unsafePerformIO? Or is there any other ways to reach my goal?

EDIT Sorry, I think I should describe my question more precisely.

Actually, I'm implementing a variation of the treap data strutcure, which needs a special 'merge' operation. It relies on some randomness to guarentee amortized O(log n) expected time complexity.

I've tried to use a pair like (Tree, StdGen) to keep the random generator for each treap. When inserting a new data to the treap, I would use random to give random value to the new node, and then update my generator. But I've encountered a problem. I have a function called empty which will return an empty treap, and I used the function myStdGen above to get the random generator for this treap. However, if I have two empty treap, their StdGen would be the same. So after I inserted a data to both treap and when I want to merge them, their random value would be the same, too. Therefore, I lost the randomness which I relies on.

That's why I would like to have a somehow "global" random generator, which yields different StdGen for each call, so that each empty treap could have different StdGen.


Solution

  • This is not a good use of unsafePerformIO.

    The reason you see the same number repeatedly in GHCi is that GHCi itself does not know that the value is impure, and so remembers the value from the last time you called it. You can type IO commands into the top level of GHCi, so you would expect to see a different value if you just type getStdGen. However, this won't work either, due to an obscure part of the way GHCi works involving not reverting top-level expressions. You can turn this of with :set +r:

    > :set +r
    > getStdGen
    2144783736 1
    > getStdGen
    1026741422 1
    

    Note that your impure function pretending to be pure will still not work.

    > myStdGen
    480142475 1
    > myStdGen
    480142475 1
    > myStdGen
    480142475 1
    

    You really do not want to go down this route. unsafePerformIO is not supposed to be used this way, and nor is it a good idea at all. There are ways to get what you wanted (like unsafePerformIO randomIO :: Int) but they will not lead you to good things. Instead you should be doing calculations based on random numbers inside a random monad, and running that in the IO monad.

    Update

    I see from your updatee why you wanted this in the first place.

    There are many interesting thoughts on the problem of randomness within otherwise referentially transparent functions in this answer.

    Despite the fact that some people advocate the use of unsafePerformIO in this case, it is still a bad idea for a number of reasons which are outlined in various parts of that page. In the end, if a function depends on a source of randomness it is best for that to be specified in it's type, and the easiest way to do that is put it in a random monad. This is still a pure function, just one that takes a generator when it is called. You can provide this generator by asking for a random one in the main IO routine.

    A good example of how to use the random monad can be found here.