You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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?
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.
Reacted by Chris Guiney, Daniel Martí and G. Nithin Reddy
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
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.
Reacted by Eric Gouyer, Clayton Stangeland and Kyle Teague
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.
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.
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.
Activity
chrisguiney commentedon Oct 19, 2022
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?
FiloSottile commentedon Oct 19, 2022
The latter. The function continues to work like before, but users are warned about it in linting, IDEs, and docs.
rsc commentedon Oct 20, 2022
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
magical commentedon Oct 20, 2022
I disagree with #54880. But i agree that, with that proposal accepted, deprecating the global Seed function is a logical step.
btracey commentedon Oct 20, 2022
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 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. IfSeedno longer works to generate a deterministic stream, then this is effectively also deprecatingrand.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.
chrisguiney commentedon Oct 20, 2022
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.
Merovius commentedon Oct 20, 2022
Personally, the main reason I want to deprecate
Seedis 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 doesn't even have to be a goroutine. It can just be a package you import which uses
initor a package you import which uses one of the globalmath/randfunctions.Personally, as I have the opposite problem, I tend to have an
internal/rndpackage 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 thatmath/randdoes. Whenever I want to get "just a random integer", I dornd.Intn(n), instead ofrand.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/rndpackage with a global locked source which is deterministically seeded to 1, can't be re-seeded and which exports the same package-scoped functionsmath/randdoes. Then replacemath/randwithinternal/rndin 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.
btracey commentedon Oct 20, 2022
I think that's a reasonable solution. I do want to say though that this workaround/solution is still implicitly deprecating
rand.Intnand friends.Merovius commentedon Oct 20, 2022
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.Seedwith a bad seed. You can't rely on them being deterministic for the same reason.If finally no one calls
rand.Seedanymore, you can finally rely on them being actually random.rsc commentedon Oct 26, 2022
In Go, deprecated means only that you should probably not use it anymore. We won't remove it, because compatibility.
17 remaining items
vcschapp commentedon Jun 27, 2023
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.
ianlancetaylor commentedon Jun 28, 2023
rand.Shuffleworks as before.If you want a repeatable shuffle, use
New(NewSource(seed))with your desired seed value to get amath.Randvalue, and call itsShufflemethod.vcschapp commentedon Jun 29, 2023
Thanks @ianlancetaylor. In retrospect I should have RTFM'd harder! I do appreciate your advice.
Stop seeding rand with timestamps, it no longer auto-seeds with a con…
Bump up min Go version to 1.20 and other dependent changes
Bump up min Go version to 1.20 and other dependent changes (#228)
Use Rand type instead of deprecated rand.Seed
Use Rand type instead of deprecated rand.Seed
[dep] Use Rand type instead of deprecated rand.Seed
[dep] Use Rand type instead of deprecated rand.Seed
[dep] Use Rand type instead of deprecated rand.Seed
math/rand: deprecate Seed