Skip to content

The destiny of randomIO and friends. #69

Description

@kindaro

A contributor says elsewhere:

… as far as random library is concerned, this global generator was already almost deprecated in 1.2.0 and we chose not to do it for now for backwards compatibility and reduction of breakage, but it definitely will be in the next major release …

The reason, as far as I can understand from the explanation given there, is that a broken value may be assigned to theStdGen in one place, making it unusable everywhere else, across all build units and scopes. It seems to be implied that randomIO and similar functions that make use of theStdGen to provide a cleaner user experience are slated for removal.

I wonder if instead theStdGen may 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. Therefore randomIO and friends could stay.

Has this been considered?

Also mentioning @lehins since he is the source of my information.

Activity

  1. changed the title [-]The way to take `randomIO` and friends in the future.[/-] [+]The destiny of `randomIO` and friends.[/+] on Jun 25, 2020
  2. lehins commented on Jun 25, 2020

    @lehins
    Contributor

    Hiding theStdGen and related functions that can modify global StdGen would make randomIO safer, but it would naturally make it less useful, since getStdGen, randomIO and randomRIO would be the only functions that survive this change.

    I think we should ask ourselves a question what does global StdGen buys 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 randomIO function and underlying global StdGen:

    1. Easy of use. The user does not even need to understand where the random value comes from in order to get a random number.
    2. Ability to access a single generator anywhere in a program where IO is 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 StdGen without sacrificing the safety and giving us many more advantages:

    1. instead of randomIO, a user can now use randomM, which will work just as well in an IO action, except that the actual generator needs to be passed as an argument
    2. Instead of grabbing a StdGen from a global mutable variable it is better to follow the ReaderT pattern 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 RandomGen or even SatefulGen. Note that current randomIO works only with StdGen.
    • Global variable that holds theStdGen can 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 ReaderT or RIO
      • or force the user to make a conscious decision about relying on this unsafePerformIO hack, but still hiding the global state from all the dependencies
    • Removing the need to define duplicate functions in random library. randomIO is really a more restricted version of randomM and getStdRandom is just applyRandomGenM fixed to StdGen

    One way or another my argument against theStdGen is 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 theStdGen API should stay it will be deprecated and eventually removed.

  3. kindaro commented on Jun 25, 2020

    @kindaro
    Author

    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 theStdGen as part of the API. My desire is exactly to avoid contact with any and all «std-gens», as much as possible. My question only concerns randomIO and 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.

  4. lehins commented on Jun 26, 2020

    @lehins
    Contributor

    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 randomIO is not one of them, despite that it has been around for so long.

  5. kindaro commented on Jun 26, 2020

    @kindaro
    Author

    I see, so basically we cannot have randomIO because it would have to be monomorphic with respect to the method of random generation, and you want to keep things class-based. Alright then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions