Skip to content

math/rand: deprecate Seed #56319

Closed
Closed

Description

@rsc

Proposal #54880, accepted last week, seeds math/rand randomly at startup. Part of the rationale is that programs that expect a specific determinstic sequence from the global rand functions (like rand.Int) are inherently fragile, since any package can change the number of times it calls those functions, which changes the results from all future calls by other packages. So programs should not depend on any kind of determinism from them.

For the same reason, I suggest we deprecate math/rand.Seed (the global seeder). Programs that need a specific deterministic sequence can do that more reliably using NewRand(NewSource(seed)), which they can be sure no other packages are consuming values from.

Activity

  1. added this to the Proposal milestone on Oct 19, 2022
  2. chrisguiney commented on Oct 19, 2022

    @chrisguiney
    Contributor

    What are the implications of deprecation? Does it simply do nothing? Is it just marked as deprecated so that IDEs/linters can usher users to the suggested usage?

  3. FiloSottile commented on Oct 19, 2022

    @FiloSottile
    Contributor

    What are the implications of deprecation? Does it simply do nothing? Is it just marked as deprecated so that IDEs/linters can usher users to the suggested usage?

    The latter. The function continues to work like before, but users are warned about it in linting, IDEs, and docs.

  4. moved this to Incoming in Proposalson Oct 19, 2022
  5. rsc commented on Oct 20, 2022

    @rsc
    ContributorAuthor

    This proposal has been added to the active column of the proposals project
    and will now be reviewed at the weekly proposal review meetings.
    — rsc for the proposal review group

  6. moved this from Incoming to Active in Proposalson Oct 20, 2022
  7. magical commented on Oct 20, 2022

    @magical
    Contributor

    I disagree with #54880. But i agree that, with that proposal accepted, deprecating the global Seed function is a logical step.

  8. btracey commented on Oct 20, 2022

    @btracey
    Contributor

    The latter. The function continues to work like before, but users are warned about it in linting, IDEs, and docs.

    So it will work into the future, just users will be warned about it? Or it will work in the near-term future, but no promises in the long run?

    programs that expect a specific determinstic sequence from the global rand functions (like rand.Int) are inherently fragile

    In scientific simulation, there are lots of cases where it is very useful to be able to produce a deterministic result, especially for debugging purposes. Right now, this is easy to do in Go. The author writes code that calls the math/rand functions, and can get reproducible behavior by setting the seed. This is incredibly useful for debugging code, for example to reliably follow a specific codepath or to check a computation by hand from some known results. It's true that this is inherently fragile, but that's not important, because during debugging I'm not changing all of that code. For working code, I do want the result to be random (and thus subject to that fragility), but for non-working code it's very useful to easily control the randomness.

    It is true that an alternative is to call rand.NewSource, but this is invasive. This call has to happen near the top of main, and then all subsequent functions have to pass around the *rand.Source. In Gonum we allow this behavior because it's intended to be a base library, but for a lot of my exploratory code I just use the default generator. If Seed no longer works to generate a deterministic stream, then this is effectively also deprecating rand.Float64() and friends for my code (as again, the deterministic behavior is really important for debugging).

    I appreciate the story is different in Go than in other languages because concurrency is so prevalent and this interacts poorly with determinism from a global generator. That said, I've had a lot of success with code that generates the necessary randomness, and then does deterministic concurrent computation given those results.

  9. chrisguiney commented on Oct 20, 2022

    @chrisguiney
    Contributor

    If Seed no longer works to generate a deterministic stream, then this is effectively also deprecating rand.Float64() and friends for my code (as again, the deterministic behavior is really important for debugging).

    I think the implication here is that if you need deterministic behavior, then you need to create a new RNG with rand.New()

    It's not really possible to ensure deterministic behavior to the global RNG, because there may be a goroutine started by a library that re-seeds it.

    I'm not sure if the go team ensures that the RNG will produce the same series for the same seed between go versions, so even if you ensure that there is no competing seed for the global RNG, the series it produces may change in the future.

    If you need a truly stable deterministic series that will hold up to changes, it should be generated, stored in the project, and read at runtime.

    That all said, while I believe the intention is that the package will continue behaving as it has, and there's just more emphasis on using it correctly. I don't know what the long term plan for the package is though, so please take my words lightly.

  10. Merovius commented on Oct 20, 2022

    @Merovius
    Contributor

    Personally, the main reason I want to deprecate Seed is so that I can run a static analyzer for anything in my transitive dependency graph calling it and filing bugs that they shouldn't do that anymore. Having the function deprecated is a great argument for that.

    It's not really possible to ensure deterministic behavior to the global RNG, because there may be a goroutine started by a library that re-seeds it.

    It doesn't even have to be a goroutine. It can just be a package you import which uses init or a package you import which uses one of the global math/rand functions.

    It is true that an alternative is to call rand.NewSource, but this is invasive. This call has to happen near the top of main, and then all subsequent functions have to pass around the *rand.Source.

    Personally, as I have the opposite problem, I tend to have an internal/rnd package which has a global locked source which is properly seeded and can't be reseeded. Apart from that it exports the same package-scoped functions that math/rand does. Whenever I want to get "just a random integer", I do rnd.Intn(n), instead of rand.Intn(n) and can rely on having a properly non-deterministic integer.

    You can do the same concept, but the other way around: Make an internal/rnd package with a global locked source which is deterministically seeded to 1, can't be re-seeded and which exports the same package-scoped functions math/rand does. Then replace math/rand with internal/rnd in any package you control and want to behave deterministically.

    This, of course, only works if all the relevant package are controllled by you, so rogue RNG usage doesn't change your output. But that's already the situation you're in.

    So, IMO 1. it's not actually invasive, really, if you don't want it to be. And 2. you should probably do the invasive thing anyway, as that's just the right thing to do.

  11. btracey commented on Oct 20, 2022

    @btracey
    Contributor

    Whenever I want to get "just a random integer", I do rnd.Intn(n), instead of rand.Intn(n) and can rely on having a properly non-deterministic integer.

    I think that's a reasonable solution. I do want to say though that this workaround/solution is still implicitly deprecating rand.Intn and friends.

  12. Merovius commented on Oct 20, 2022

    @Merovius
    Contributor

    is still implicitly deprecating rand.Intn and friends.

    I strongly disagree. On the contrary. It makes them, for most usecases - those which actually want non-deterministic random numbers - usable, as opposed to the current situation, where they are pointless for all usecases.. Currently you can't rely on them to be non-deterministic, because some library might've called rand.Seed with a bad seed. You can't rely on them being deterministic for the same reason.

    If finally no one calls rand.Seed anymore, you can finally rely on them being actually random.

  13. rsc commented on Oct 26, 2022

    @rsc
    ContributorAuthor

    So it will work into the future, just users will be warned about it? Or it will work in the near-term future, but no promises in the long run?

    In Go, deprecated means only that you should probably not use it anymore. We won't remove it, because compatibility.

  14. 17 remaining items

  15. vcschapp commented on Jun 27, 2023

    @vcschapp

    Does the proposal cover what to do with rand.Shuffle?

    It's always a bit frustrating when one part of the standard library specifically documents reliance on a feature marked deprecated by the other part.

    
    
  16. ianlancetaylor commented on Jun 28, 2023

    @ianlancetaylor
    Contributor

    rand.Shuffle works as before.

    If you want a repeatable shuffle, use New(NewSource(seed)) with your desired seed value to get a math.Rand value, and call its Shuffle method.

  17. vcschapp commented on Jun 29, 2023

    @vcschapp

    Thanks @ianlancetaylor. In retrospect I should have RTFM'd harder! I do appreciate your advice.

  18. removed this from Proposalson Dec 4, 2023
  19. added a commit that references this issue on Feb 20, 2024
    ad7dc8a
  20. locked and limited conversation to collaborators on Jun 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions