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

I want to have a function that returns different stdGen

in every call without I / O. I tried to use unsafePerformIO

like 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. Am I abusing unsafePerformIO

? Or are there other ways to achieve my goal?

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

In fact, I am implementing a strapcure treap variation that requires a custom merge operation. It relies on some randomness to guarantee O (log n) amortization of the expected time complexity.

I tried using a pair, for example (Tree, StdGen)

, to keep a random generator for each binding. When inserting new data into the treap, I would use random

to give a random value to the new node and then update my generator. But I ran into a problem. I have a function called empty

that will return an empty treap and I used the function myStdGen

above to get a random generator for this treap. However, if I have two empty treap, they stdGen

will be the same. So after I have inserted the data both times, and when I want to concatenate them, their random value will be the same. So I lost the chance that I rely on.

This is why I would like to have some sort of "global" random generator that gives different stdGen

for each call, so that each empty treap can have a different one stdGen

.

+3


source to share


3 answers


It is not very convenient to use unsafePerformIO

.

The reason you see the same number in GHCi repeatedly is because GHCi itself does not know the value is impure and therefore remembers the value from the moment you called it. You can enter I / O commands at the top level of GHCi, so you expect to see a different value if you just type getStdGen

. However, this won't work either due to the obscure part of how GHCi works without returning a top-level expression. You can enable this with :set +r

:

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

      

Note that your impure function pretending to be clean is still not working.

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

      

You really don't want to go that route. unsafePerformIO

it is not intended to be used in this way, and it is not a good idea. There are ways to get what you wanted (for example unsafePerformIO randomIO :: Int

), but they will not lead you to good things. Instead, you should be performing calculations based on random numbers inside a random monad, and run that on the IO monad.



Update

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

There are many interesting considerations on the randomness issue in other seemingly transparent functions in this answer .

While some people are in favor of using it in this case unsafePerformIO

, it is still a bad idea for a number of reasons, which are outlined in different parts of this page. After all, if a function depends on a source of randomness, it is best to specify the type in it, and the easiest way to do this is to put it in a random monad. This is still a pure function, just one, that the generator accepts when it is called. You can provide this generator by requesting a random one in the main routine IO

.

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

+7


source


I have abused unsafePerformIO

Hell! The "hallmarks of pure function" are

  • No side effects
  • Referentially transparent , i.e. each subsequent evaluation of the result should give the same result.

There is actually a way to achieve your "goal", but the idea is simply wrong.

myStdGen :: () -> StdGen
myStdGen () = unsafePerformIO getStdGen

      

Since this is a (useless) function call instead of CAF , it will evaluate the action IO

on each call separately.

However, I think the compiler can optimize this pretty much, so definitely don't rely on it.

EDIT when trying to notice that getStdGen

itself always yields the same generator when used within the given process, so even if the above uses smarter types it won't work.




Note that the correct use of pseudo-randomness in algorithms, etc. Doesn't need IO everywhere - for example, you can manually seed StdGen

, but then propagate it correctly with split

, etc. A nicer way to handle what's with the randomness of the monad . The program as a whole will always give the same result, but internally has all the different random numbers needed for useful work.

Alternatively, you can get a generator from IO, but still write your algorithm in a pure random monad, not IO

.


Another way to get "random" in a completely pure algorithm: requires the input to be Hashable

! Then you can effectively use any argument to the function as a random seed. This is a bit of a strange solution, but might work for your treap application (although I believe some people no longer classify it as a treap, but as a special kind of hashmap).

+8


source


Yes, you have abused unsafePerformIO

. There are very few good reasons to use unsafePerformIO

, for example when interacting with a C library, and also in implementing several core libraries (I think this ST

is one of them). In short, don't use unsafePerformIO

unless you're really sure what you are doing. It is not designed to generate random numbers.

Recall that functions in Haskell must be pure, which means that they only depend on their inputs. This means that you can have a pure function that generates a "random" number, but that number depends on the random generator you pass to it, you can do something like

myStdGen :: StdGen
myStdGen = mkStdGen 42

      

Then you could do

randomInt :: StdGen -> (Int, StdGen)
randomInt g = random

      

But then you have to use the new StdGen

one returned from that function while moving forward, or you will always get the same result from randomInt

.


So, you might be wondering, how do you randomly generate random numbers without resorting to IO

? The answer is a monad State

. He looks like

newtype State s a = State { runState :: s -> (a, s) }

      

And his monad instance looks like

instance Monad (State s) where
    return a = State $ \s -> (a, s)
    (State m) >>= f = State $ \s -> let (a, newState) = m s
                                        (State g)     = f a
                                    in g newState

      

It's a bit confusing to look at the first time around, but essentially the entire state monad is a fancy composition of functions. See LYAH for details . It is important to note that the type s

does not change between steps, only the parameter a

can change.

You will notice that it is s -> (a, s)

very similar to our function StdGen -> (Int, StdGen)

, with s ~ StdGen

and a ~ Int

. This means that if we did

randomIntS :: State StdGen Int
randomIntS = State randomInt

      

Then we could do

twoRandInts :: State StdGen (Int, Int)
twoRandInts = do
    a <- randomIntS
    b <- randomIntS
    return (a, b)

      

Then it can be started by providing the initial state:

main = do
    g <- getStdGen
    print $ runState twoRandInts g

      

StdGen

still emerges from IO

, but then all logic itself occurs in the state monad purely.

+5


source







All Articles