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
.
Do I abused
unsafePerformIO
Heck yes! The "distinguishing features of a pure function" are
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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With