Repository navigation
The destiny of randomIO and friends. #69
Description
Activity
- changed the title
[-]The way to take `randomIO` and friends in the future.[/-][+]The destiny of `randomIO` and friends.[/+]on Jun 25, 2020 Hiding
theStdGenand related functions that can modify globalStdGenwould makerandomIOsafer, but it would naturally make it less useful, sincegetStdGen,randomIOandrandomRIOwould be the only functions that survive this change.I think we should ask ourselves a question what does global
StdGenbuys us that can't be done without it. We simply need to weigh all pros and cons of that API, right? I encourage everyone to add their 2¢ in on this.The are only two pros that I can see for a
randomIOfunction and underlying globalStdGen:- Easy of use. The user does not even need to understand where the random value comes from in order to get a random number.
- Ability to access a single generator anywhere in a program where
IOis possible.
There a requite a few cons, but I am not going to reiterate the points that I have already mentioned here #57 (comment) and here #57 (comment)
I would like to step back and suggest that new interface that was introduced in random-1.2.0 make is just as easy to use as the interface provided by the global
StdGenwithout sacrificing the safety and giving us many more advantages:- instead of
randomIO, a user can now userandomM, which will work just as well in an IO action, except that the actual generator needs to be passed as an argument - Instead of grabbing a
StdGenfrom a global mutable variable it is better to follow theReaderTpattern or even implicit parameters, if that is your thing. (FYI @kindaro just to prevent you from getting hung up on the words again, by "your thing" I mean a user not you personally)
Why am I saying these approaches are better than the outdated use of global mutable variable:
- It will work with any random number generator that implements
RandomGenor evenSatefulGen. Note that currentrandomIOworks only withStdGen. - Global variable that holds
theStdGencan make debugging a nightmare in a more complex program with many dependencies that make use of that variable - If the user still wishes to have access to such global generator he can always create one himself:
myGlobalStdGen :: AtomicGenM StdGen myGlobalStdGen = unsafePerformIO (newAtomicGenM . mkStdGen . round =<< getPOSIXTime) {-# NOINLINE myGlobalStdGen #-}
and use it with all of the nice functions that are now provided by the library:
λ> randomM myGlobalStdGen :: IO Int -142028325767399101 λ> uniformRM (10, 100) myGlobalStdGen :: IO Int 99 λ> uniformListM 10 myGlobalStdGen:: IO [Word8] [152,147,159,234,64,177,83,166,11,231]
The important part with such approach is that we are:
-
giving the user the option to either:
- do it the proper way with passing the generator as an argument to a function or thread it through an the environment with
ReaderTorRIO - or force the user to make a conscious decision about relying on this
unsafePerformIOhack, but still hiding the global state from all the dependencies
- do it the proper way with passing the generator as an argument to a function or thread it through an the environment with
-
Removing the need to define duplicate functions in
randomlibrary.randomIOis really a more restricted version ofrandomMandgetStdRandomis justapplyRandomGenMfixed toStdGen
One way or another my argument against
theStdGenis that the approach is obsolete and should be discouraged. It probably was cool back 20 years ago when it was added to the library, but I think nowadays we have much better tools at our disposal and can avoid such hacks in our code.@kindaro thank you for opening this ticket. We'll let this issue cook until next major release and unless someone comes up with a good reason why this
theStdGenAPI should stay it will be deprecated and eventually removed.Thanks @lehins, but I do not think you address the concern I am raising. Your answer seems to be directed at someone you maybe had a conversation with previously and elsewhere. I am not that person.
Particularly, I am not raising the question of exposing
theStdGenas part of the API. My desire is exactly to avoid contact with any and all «std-gens», as much as possible. My question only concernsrandomIOand similar functions.I appreciate the ease of use they afford and I think that is a benefit that should not be understated. There is any number of programs that only need basic random facilities. If generating a few random numbers can be made simple and safe, I think it is worth an effort.
Of course I allow the possibility that there is no safe way to have
randomIO— if there is an argument to that end, let it be announced.Your answer seems to be directed at someone you maybe had a conversation with previously and elsewhere. I am not that person.
My answer is directed at anyone who will be reading this issue. You are simply one of very many users of this library and my goal is not to convince you or provide any sort of proofs for you. My responsibility is to listen to everyone's input, including yours and together with other maintainers of this library make future design decisions.
My desire is exactly to avoid contact with any and all «std-gens», as much as possible.
I do understand your desire now. That is not the goal of this library. In fact it is quite the opposite, the goal is to provide an interface for various implementation of pseudo random number generator algorithms available and make them usable through one unified interface.
I am sure that there are others out there who don't care about "how" and have a simple need for an occasional few random numbers. I would encourage you to create such a library.
This is the defacto random number generation library and we strike to promote the correct and idiomatic Haskell usage patterns. I hate to say it once again, but
randomIOis not one of them, despite that it has been around for so long.I see, so basically we cannot have
randomIObecause it would have to be monomorphic with respect to the method of random generation, and you want to keep things class-based. Alright then.
A contributor says elsewhere:
The reason, as far as I can understand from the explanation given there, is that a broken value may be assigned to
theStdGenin one place, making it unusable everywhere else, across all build units and scopes. It seems to be implied thatrandomIOand similar functions that make use oftheStdGento provide a cleaner user experience are slated for removal.I wonder if instead
theStdGenmay be made internal, allowing it to be used only by functions defined in the library itself. Then it could be made sure that it cannot be broken. ThereforerandomIOand friends could stay.Has this been considered?
Also mentioning @lehins since he is the source of my information.