Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(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.

like image 911
arbuztw Avatar asked Nov 30 '22 01:11

arbuztw


1 Answers

Do I abused unsafePerformIO

Heck yes! The "distinguishing features of a pure function" are

  • No side-effects
  • Referentially transparent, i.e. each subsequent eval of the result must yield the same.

There is in fact a way to achieve your "goal", but the idea is just wrong.

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

Because this is a (useless) function call instead of a CAF, it'll evaluate the IO action at each call seperately.

Still, I think the compiler is pretty much free to optimise that away, so definitely don't rely on it.

EDIT upon trying I noticed that getStdGen itself always gives the same generator when used within a given process, so even if the above would use more reasonable types it would not work.


Note that correct use of pseudorandomness in algorithms etc. does not need IO everywhere – for instance you can manually seed your StdGen, but then properly propagate it with split etc.. A much nicer way to handle that is with a randomness monad. The program as a whole will then always yield the same result, but internally have all different random numbers as needed to work usefully.

Alternatively, you can obtain a generator from IO, but still write your algorithm in a pure random monad rather than IO.


There's another way to obtain "randomness" in a completely pure algorithm: require the input to be Hashable! Then, you can effectively use any function argument as a random seed. This is a bit of a strange solution, but might work for your treap application (though I reckon some people would not classify it as a treap anymore, but as a special kind of hashmap).

like image 135
leftaroundabout Avatar answered Dec 05 '22 05:12

leftaroundabout