golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
122.86k stars 17.52k forks source link

math/rand: seed global generator randomly #54880

Closed rsc closed 1 year ago

rsc commented 2 years ago

As a step toward a larger overhaul of math/rand (for example, #21835 or #26263), @robpike and I propose that math/rand seed the global generator randomly and deprecating Seed, rather than behaving as if Seed(1) had been called. A more aggressive step (which we are not proposing at the moment) would be to make the global Seed function a no-op.

Seeding the global generator randomly at startup eliminates the possibility of programs that don't call Seed depending on the algorithm behind the global functions. If the package randomly seeded the global generator, that would let us change the global generator's source algorithm as an implementation detail: nothing could tell that the algorithm was different.

Auto-seeding would also fix any programs that are missing calls to Seed and think they are getting non-deterministic values but are not. (This is a common mistake.)

Auto-seeding would break programs that have no calls to Seed and expect a deterministic sequence. The most common time this might happen would be in golden tests.

There are lots of other possible changes that could be made to math/rand, and we are intentionally leaving them all to the side for the purposes of this proposal. This proposal is only about auto-seeding, precisely because it targets the most common backwards compatibility concern that comes up in any other changes to math/rand. Addressing that one concern in isolation greatly simplifies any other changes we might make later.

I hope that this proposal discussion can focus on the impact of making this change, specifically programs that would break that we need to be concerned about. If it turns out that we can't make this change, then maybe we will propose a math/rand/v2 instead. But we'd like to understand whether it can be done in the original math/rand first.

Again please limit comments to the impact of auto-seeding and not on other possible changes to math/rand. Thank you!

dominikh commented 2 years ago

While I am sympathetic to the idea, I believe this would be a violation of the backwards compatibility guarantee in a very direct manner. The package documentation clearly states that

Top-level functions, such as Float64 and Int, use a default shared Source that produces a deterministic sequence of values each time a program is run. Use the Seed function to initialize the default Source if different behavior is required for each run.

That is, the fact that the global RNG is deterministic isn't an implicit detail that people may be relying on; it's documented fact.

I don't believe that the downsides of accidentally using an unseeded RNG outweigh the cost of breaking compatibility, especially because this can't be considered a bug fix nor a security fix.

zhangyunhao116 commented 2 years ago

I guess the final implementation may be similar to https://github.com/zhangyunhao116/fastrand ? The internal implementation of this library has been widely used in my company, and we have proposed similar ideas internally, but we have not implemented it yet(replace math/rand with this library).

According to our experience, this change will not bring any negative effects in most cases(maybe ~99%?). In rare cases, this may break some services or tests(e.g., call Seed(1) and then generate a random slice, use it as a test case or send it to other places).

But I think it's not worthing to break the compatibility for golang/go, as @dominikh said before. I think math/rand/v2 or math/fastrand is better.

robpike commented 2 years ago

It's a bit of a stretch, but it can be argued that this is in fact a security issue. Programs that don't seed their random numbers will behave predictably, which can sometimes be a risk if the numbers are used to generate unique, supposedly unguessable results.

It was a design error, copied from Unix, to have rand be reproducible like this. It should be changed. The question is whether it can be changed without a version bump on the library.

atdiar commented 2 years ago

I would add this as a math/rand2 variant. I personally have used the behaviour in the past to generate stable id references for different runs of a same program.

When I don't need that behavior, I usually go for devurandom.

Unless I'm mistaken somewhere?

Edit: ha but I usually provide a seed. Please ignore sorry.

kortschak commented 2 years ago

Programs that don't seed their random numbers will behave predictably, which can sometimes be a risk if the numbers are used to generate unique, supposedly unguessable results.

I'd say that if people are using math/rand [out of the box] for security-sensitive uses there are other problems.

dallbee commented 2 years ago

Go1 compatibility promise aside (and I do think this would be a compatibility break),

Seeding math/rand doesn't make it any less predictable. Instead, it just becomes easier to use as a footgun in security crtical contexts.

Today, it's common for junior devs to ask me why math/rand produces the same values every time they use it. This is a great learning point - they become aware of the difference between a PRNG and a CSPRNG.

Autoseeding would make it easy to think math/rand is suitable for all uses.

seebs commented 2 years ago

This would definitely break some existing programs, even if those programs are bad. I think, though, dallbee's point is the one I'm most concerned by. I am worried about anything that makes it easier for people to accidentally get apparently-not-horrible behavior from the default rand, without actually getting good enough behavior that it's reasonably safe. I'd rather have the behavior be sufficiently clearly bad that people are encouraged to change it.

srowles commented 2 years ago

It's just an anecdote, but I tinker with procedural content generation toys from time to time, these are often UI based or 1-shot programs that provide an output. I often use the global random in those pieces of code and I initialise the seed based on some input.

Default seeding the global generator would not break my code, but making the global Seed() call a no-op would definitely break the code today I have today.

I also agree with the other comments that this should not be changed as it breaks documented behaviour (https://pkg.go.dev/math/rand#Seed)

I don't see that it's a security issue because nobody should be using math/rand for security related random and in fact the doc's for math/rand even explicitly cover this:

"For random numbers suitable for security-sensitive work, see the crypto/rand package."

rsc commented 2 years ago

Re security:

There's no positive security argument here. Code that needs true randomness should use crypto/rand.

But there's also no negative security argument here. People accidentally reaching for math/rand when they mean crypto/rand does not seem like justification for making math/rand as bad as possible.

rsc commented 2 years ago

Re general breakage and backwards compatibility:

@dominikh pointed out

Top-level functions, such as Float64 and Int, use a default shared Source that produces a deterministic sequence of values each time a program is run. Use the Seed function to initialize the default Source if different behavior is required for each run.

He reads that as a promise, but I read it as a warning (don't forget to call Seed).

Either way, using the global source and expecting a deterministic sequence is incredibly fragile. All it takes is one new import that happens to call Seed or Int (or any of the other getters) and your code breaks. Suppose instead of auto-seeding we made a change where some commonly used package (say, strings) called rand.Int a few times at init time. Is changing the number of times math/rand is called by a package's init function a breaking change for that package? That can't be right.

Auto-seeding is like you have a package linked into the binary that reads a non-deterministic amount of randomness from math/rand at init time.

To be clear, at the moment I am assuming that if you call global Seed(1) then determinism is restored. Programs that want determinism can and probably should ask for it explicitly.

rsc commented 2 years ago

Re specific breakage:

@zhangyunhao116 reported their experience:

According to our experience, this change will not bring any negative effects in most cases(maybe ~99%?). In rare cases, this may break some services or tests(e.g., call Seed(1) and then generate a random slice, use it as a test case or send it to other places).

In contrast, @seebs said:

This would definitely break some existing programs, even if those programs are bad.

I expect it would break some (again, latently fragile) golden tests. Those are annoying but easy to fix. What about non-tests? Can anyone think of a case where a non-test would break? That would concern me more.

dr2chase commented 2 years ago

Regarding security and warning new programmers of foot-guns, if that is a problem, the safer solution is to make the default RNG cryptographically secure, and when it shows up in profiles, then we have the discussion about whether this application needs the secure rand or not. Premature optimization, etc.

I favor this change. Current behavior is a foot-gun, adds to the learning curve for new Go programmers, adds one line of "don't forget" code to good programs, and it compromises security, where one of the rules (at least, my rules) for security is "you never know what's going to end up exposed to strangers on the internet".

I am less swayed by the need for compatibility here because the winning example is "tests". Those will break, they'll be easy to fix, it's unlikely that production code depends on this, and my actually-educated guess is that any such uses are unintentional and this change will fix (through removed security holes) more code than it breaks.

I am not sure what to do with Seed. Short-term, we'll need "Seed(1)" to fix any tests that this breaks, but then what's the plan for changing the RNG down the road, if that becomes necessary, or or if people are swayed by my Great Idea in the first paragraph? Special case Seed(1) to select the old RNG?

srowles commented 2 years ago

Regarding security and warning new programmers of foot-guns, if that is a problem, the safer solution is to make the default RNG cryptographically secure

Pseudo random has use-cases beyond security use cases, using it to produce "random" but deterministic output. This is useful for a wide range of scenarios.

Pseudo random shouldn't be removed from the core library just because some use cases are better with cryptographic random, not everything needs or wants real random behaviour or the overhead of secure random when pseudo is good enough.

dallbee commented 2 years ago

@rsc said:

Either way, using the global source and expecting a deterministic sequence is incredibly fragile...

Perhaps bordering on counter-proposal territory, but maybe having a global PRNG is the problem here?

Leave the behavior alone, deprecate it, put up big warning signs that users should initialize their own source. Expose LockedSource for convenience.

I think it's pretty valid that folks here are wanting a PRNG to produce a deterministic sequence. I also appreciate that the global source is fragile and can easily be misused.

rsc commented 2 years ago

Perhaps bordering on counter-proposal territory, but maybe having a global PRNG is the problem here?

Having a global deterministic PRNG is the problem. If it's not guaranteed to be deterministic, there are many possible paths forward.

rsc commented 2 years ago

I am not sure what to do with Seed. Short-term, we'll need "Seed(1)" to fix any tests that this breaks, but then what's the plan for changing the RNG down the road, if that becomes necessary

The plan is that Seed would do two things: (1) select the original algorithm as the global source and (2) seed it. Until Seed is called, you get the better algorithm.

rsc commented 2 years ago

I forgot to mention that if you want to try the change out, you can patch in https://go-review.googlesource.com/427963, or as a rough approximation you can put

func init() { rand.Seed(time.Now().UnixNano()) } 

in any package you like.

josharian commented 2 years ago

Can anyone think of a case where a non-test would break?

I can imagine using math/rand to do deterministic-yet-random codegen for correctly-shaped random input data for a simulation. Broadly speaking, the line between tests and prod is looser in many contexts in which math/rand gets used.

But to be fair, I don’t know of any cases concretely.

josharian commented 2 years ago

It’s also worth noting that this proposal is a bit of a surprise. There have been multiple discussions about de-global-lock-ing math/rand over many years, always with the apparent understanding that we couldn’t change the math/rand value stream. (I’m on my phone, but I believe this is documented in #26263, at least as of the time of that issue.) It might be helpful to share some context about why your thinking has evolved.

beoran commented 2 years ago

First of all, a non cryptographical, seedable RNG like that of math/rand is useful not only for tests, but also for games , simulations, and scientific models. The sequence should be predictable in some use cases, so we definitely need the Seed function to stay functional.

I am not sure about the impact on backwards compatibility. The documentation does not exactly say what random series you are getting by default. Is it the same as after calling Seed(0)?

And I assume there was some serious security issue related to this that triggered this issue? It is documented well enough that we cannot use math/rand for secure random numbers, so I am more agreeing with those who say crypto/rand should have been used in stead.

If the issue is that too many people ignore this warning, then we can deprecate the whole package and move it to a new package a bit like ioutil.

rsc commented 2 years ago

In #21835, @robpike wrote on Sept 11 2017:

We propose to make this generator the default one in math/rand for Go 2. The Go 1 compatibility decree could allow it to happen today, but we have chosen in the past to take the guarantee more literally than is expressed by also requiring that the generated stream of bits will never change. This decision was made when a fix was done, but enough tests in production were discovered that depended on the exact stream that it was deemed simpler to lock down the implementation rather than to fix all the tests.

@josharian asked much more recently:

It’s also worth noting that this proposal is a bit of a surprise. There have been multiple discussions about de-global-lock-ing math/rand over many years, always with the apparent understanding that we couldn’t change the math/rand value stream. (I’m on my phone, but I believe this is documented in #26263, at least as of the time of that issue.) It might be helpful to share some context about why your thinking has evolved.

It came up because Rob and I were discussing math/rand as an example of a possible v2 package in the standard library (it is a good test case for v2 machinery precisely because it is so simple). We've meant to replace the generator for a long time, and if we do that it would be good to avoid making the same mistakes over again. But then we also got to talking about how much we can fix the current math/rand without a v2.

As Josh noted, the stumbling block for lots of possible API evolution has been the default Seed(1) behavior. So we thought it made sense to file a proposal focused on really understanding the true amount of flexibility we have for just that one detail, separate from any other API changes.

One thing that has changed since 2017 is we have five more years of experience with programmers use of math/rand. It does get used in many reasonable contexts, but what we see over and over is (1) programmers forgetting to Seed; (2) programmers seeding in their package "just in case"; (3) programs breaking when a program that did (1) but also imported a package that did (2) removes the import; and so on.

To be clear, the outcome is not a foregone conclusion here. But at the same time, I haven't seen compelling specifics of breakage yet either.

rsc commented 2 years ago

I meant to add that I don't have any records of which tests Rob was talking about in the 2017 comment, but I am rerunning all of Google's Go tests with an init-time auto-seeding change and will report what I find. If anyone else has large code bases and can do the same, more data is always good.

rsc commented 2 years ago

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

kortschak commented 2 years ago

While I'm not convince of the need of the change and I do find the comment above (https://github.com/golang/go/issues/54880#issuecomment-1237699275) compelling from a pedagogical perspective, the autoseeding should not break any reasonable simulation as anyone doing a reasonable simulation would already have included an option to set the seed since a single run with a seed would not be strong enough for any conclusions of a simulation to be drawn. I'd also note that for any reasonable simulation code the author should probably be using their own local PRNG source rather than the package source to avoid locking costs, and the non-reproducibility that would come from sharing random sources between concurrent execution threads.

beoran commented 2 years ago

Just an experiment that shows that now, not seeding the RNG and generating numbers exactly the same as generating numbers after seeding it with Seed(1). I do wonder why it is not Seed(0)?

https://go.dev/play/p/Qph7qlRbjUg

Anyway, randomly seeding the RNG alone doesn't seem like a big backwards compatibility problem, as the sequence was not specified, and we can always call Seed(1) to get the original sequence back.

rsc commented 2 years ago

I meant to add that I don't have any records of which tests Rob was talking about in the 2017 comment, but I am rerunning all of Google's Go tests with an init-time auto-seeding change and will report what I find. If anyone else has large code bases and can do the same, more data is always good.

I ran all of Google's Go tests with auto-seeding, and the failed test rate was about 2 per 10,000. The failures were all golden test failures, as one would expect.

frioux commented 2 years ago

I just want to go on the record that this seems like a clear improvement, makes the generation of random numbers or picking a user at random or whatever much easier, and is welcome from me and mine.

ianlancetaylor commented 2 years ago

Just to look at some other languages:

So Go copied C's seed behavior, but it seems that several other languages did not.

Deleplace commented 2 years ago

For reference, a very related 2015 discussion: #11871 math/rand: Deterministic random by default is dangerous

dallbee commented 2 years ago

Thanks for bringing that up, @Deleplace.

I tend to agree with the comment @rsc left in 2015:

If there is concern about people incorrectly using math/rand in security settings, it seems like it would be better to have the same seed every time than to give the appearance of unpredictability. That is, it is better without any attempt at time-dependent or otherwise unpredictable automatic seeding.

Of course, that was ~7 years ago! If I may ask, @rsc, what insight or experience has changed your point of view in that time?

rsc commented 1 year ago

The strongest argument I can see here is the one I mentioned above in https://github.com/golang/go/issues/54880#issuecomment-1238210588, namely that as it stands today, depending on the deterministic global generator in math/rand leads to bad results in package evolution.

For example, if I have package P and it uses rand.Int 100 times in its func init (without seeding), and I change it to call rand.Int only 99 times, then any package importing P and depending on the exact rand results from its own calls to math/rand will break when it updates from the old P to the new P. Does that mean P has to issue a v2 every time it changes the number of rand calls it makes during init? Or during other functions that might be called? This seems clearly false.

But if P changing the number of calls is not a breaking change, then changing the global generator to be non-deterministically seeded should also not be a breaking change.

Or put a different way, suppose we wanted to use rand.Int in a standard library package for some randomized algorithm. Would that be allowed, or would that be a breaking change because these programs that depend on the exact results from the global generator would be affected? It seems like that can't be a breaking change. But if it's not, then neither is randomizing the seed.

Yes, this will break programs (mostly tests) that depend on the global sequence. Those programs are very fragile today - changes in other packages can break those programs. (To some extent, they're broken already and don't know it.) Instead, those programs can allocate their own source and seed with 1 to get that sequence again. And then they will be insulated from any of these other breakages due to other uses of the global sequences.

On top of all this, non-deterministically seeding will open the door for various other improvements inside the package.

kortschak commented 1 year ago

Does that mean P has to issue a v2 every time it changes the number of rand calls it makes during init? Or during other functions that might be called? This seems clearly false.

I don't think this is so clear. RAA can be faced in the opposite direction here if flexibility in the starting axiom is allowed. If conistent behaviour is the axiom we can arrive at the conclusion that it's not false and that libraries should not depend on calls to the global rand. This is the conclusion that we arrived at in Gonum where all the simulation primitives take a rand.Source so that the problem never arises.

Or put a different way, suppose we wanted to use rand.Int in a standard library package for some randomized algorithm. Would that be allowed

I don't think it should be and there are better ways of implementing this kind of thing (above).

On top of all this, non-deterministically seeding will open the door for various other improvements inside the package.

This is a convincing argument, except that the major improvements like rand source width are constrained by interface satisfaction, viz math/rand cf x/exp/rand.

The issue of how making something look like it's good enough but isn't will impact on use hasn't been addressed.

rsc commented 1 year ago

Based on the discussion above, this proposal seems like a likely accept. — rsc for the proposal review group

kortschak commented 1 year ago

What were the parts that make this likely accept? It seems much less clear to me.

mvdan commented 1 year ago

I ran all of Google's Go tests with auto-seeding, and the failed test rate was about 2 per 10,000. The failures were all golden test failures, as one would expect. [...] Yes, this will break programs (mostly tests) that depend on the global sequence.

Do we have a larger corpus of Go packages to test with auto-seeding turned on? That would presumably give us more confidence about the belief that we would only break a minority of tests.

We could always try this change during the beta or RC period and see how many users report bugs, although the reality today is that not very many people test pre-releases regularly.

Merovius commented 1 year ago

@rsc

Either way, using the global source and expecting a deterministic sequence is incredibly fragile. All it takes is one new import that happens to call Seed or Int (or any of the other getters) and your code breaks.

I believe the same argument can be made about this proposal, unless Seed is removed altogether. Say we do this and you import one package that happens to call Seed(1), suddenly you unexpectedly get a deterministic sequence.

I wrote my own wrapper with a locked source without any Seed function for exactly this reason. As a library author, I can't use the global math/rand functions because I can't rely on them being seeded. I can't seed them because I might break someone relying on them being deterministic. And as a main author I can't use them because I have to assume an author of some package I import thought it is appropriate to seed it with the current time or some other bad seed.

The only way to get a reliable, properly non-deterministic PRNG is to use one that can't be seeded at all.

I generally would like something like this proposal to happen, but I neither like the breakage, nor does it seem to go far enough. I'd rather have something like

// SeedRandomly chooses a non-deterministic seed for the global source
// and makes any future call to `Seed` a no-op.
func SeedRandomly()

which I can call from main for much the same effect. It would be backwards compatible, it would allow me to trust that the seed actually remains good and it would still allow us to change the algorithm for the global source, at least. What it wouldn't do is prevent people from accidentally forgetting to call it and getting deterministic sequences. But at least some people seem to think that's a good thing, with the whole "at least it's obviously broken" argument.

rsc commented 1 year ago

What were the parts that make this likely accept? It seems much less clear to me.

The main reason suggested for not making this change is that it will break fragile programs that depend on the exact sequence of results from top-level rand functions. But that reason doesn't hold up. Adding new calls to rand.Int or init funcs in the standard library would break the same programs, and yet we clearly must be able to make that kind of change. It would make no sense to say we can't use functionality that the standard library provides. It's true that programs that call Seed(1) go back to being the same kind of fragile, but at least Seed(1) is a fairly explicit signal about opting in to fragile behavior. It also seems to me that if we can break fragile programs by changing the results from sort.Sort, as we did in Go 1.19, then we should also be able to break fragile programs by changing the results from rand.Int.

The only other reason suggested for not making the change was security, but I don't think that holds up either. The ability to misuse a package in one context should not mean we have to keep it difficult to use in other contexts.

I don't believe any other reasons have been brought up.

On the other side, making the change fixes programs that think they are getting non-deterministic results but aren't (a common mistake), and it opens the door to more efficient implementations of the top-level functions. And we also have evidence from one large code base that making the change broke very few tests.

If anyone else with a large codebase wants to try the change and report about test breakage, that would be useful data. (Add a func init() { rand.Seed(time.Now().UnixNano()) } to the top of a commonly-imported package or two in your repo.)

kortschak commented 1 year ago

I don't believe any other reasons have been brought up.

https://github.com/golang/go/issues/54880#issuecomment-1237699275

rsc commented 1 year ago

@kortschak it looks like you are referring to this part of that comment:

Today, it's common for junior devs to ask me why math/rand produces the same values every time they use it. This is a great learning point - they become aware of the difference between a PRNG and a CSPRNG.

Maybe that's useful but I'm not sure it's a compelling enough reason not to make the change. The core of the argument is that the current behavior is wrong in a few different ways. Fixing wrong behavior usually wins over what people learn from running into the wrong behavior.

kortschak commented 1 year ago

More "Autoseeding would make it easy to think math/rand is suitable for all uses."

There are already significant misunderstandings of the contexts where the two packages should be used (partly impacted by the way CRNGs and PRNGs are used in other languages I think), and I think this will make that worse.

rsc commented 1 year ago

@kortschak OK, I think that's the security argument then. Note that issue #20661 is deprecating math/rand.Read, which is by far the most common way that users accidentally use a pseudo-random source when they should be using a true random source. If #20661 is accepted (which is currently "likely"), I think it takes with it almost the entire security argument.

rsc commented 1 year ago

Replying to @Merovius, I like the idea of rand.AutoSeed, but looking more closely I don't think it solves the problem.

Consider a use of rand in a library, like net/http or testing, which both use it today. If the library calls rand.AutoSeed we're back to automatic seeding in all programs. So it must be package main's responsibility to call rand.AutoSeed. But even if main's init calls AutoSeed, that runs after all the inits in all the other packages. So any init code that calls rand is not getting auto-seeded results. The only way to get auto-seeded results at init time is for the library to call rand.AutoSeed, and we're back where we started.

Merovius commented 1 year ago

@rsc The benefit I really want from it is to prevent future Seed calls from doing anything, so I can be sure that the global source is always properly seeded. That is, we wouldn't be quite back where we started - we'd get the same level of breakage, but there is some benefit.

I agree that it's unfortunate though, that there would be no way to guarantee this happens before imported packages's init, so there is still a window of them getting bad randomness.

rsc commented 1 year ago

I agree about it being nice to remove the global func Seed entirely. Perhaps in math/rand/v2. Right now I'm focusing on what we can do in math/rand without a v2. The next step will be to propose a math/rand/v2 that removes problematic API. Making compatible improvements to the existing math/rand first ensures that the delta between the two packages is as small as possible.

rsc commented 1 year ago

Leaving in likely accept for another week.

dr2chase commented 1 year ago

@kortschak Thus my proposal that the default RNG should be secure. Most people won't notice that an RNG is insecure, even if it matters, so it can stay broken. If on the other hand it is slow and that matters, they notice that pretty quickly, and will deal with it, hopefully in an appropriate way. It is useful to us to have become experts and we place value on all the time it took for that to happen, but the more we can lower the level of expertise needed to write secure and mostly bug-free Go, the better. And in all real-world cases, I prefer a performance foot-gun to a security foot-gun.

kortschak commented 1 year ago

@dr2chase Unfortunately that doesn't consider the use of PRNGs in simulation and randomised algorithms. Particularly in simulations, people would like to be able to replay a simulation repeatably.

The situations where seeding changes, like those proposed here, impact on those uses are likely to be very few, but if the default RNG is not a PRNG, there would be many more cases where people who have code that they expect to give consistent output for the same input, but don't. Now, arguably, they should not be using a program-global RNG for the simulation, but it happens, and if people always did the right thing, we wouldn't be having this discussion.

dr2chase commented 1 year ago

@kortschak a non-repeatable simulation is another one of those in-your-face problems that you notice and fix. It's much easier to get educated about things like that, and performance, because the failure is not subtle.

And if it's a multithreaded simulation, you'll want something like splittable PRNG, wouldn't you? E.g. https://vigna.di.unimi.it/ftp/papers/LXM.pdf

kortschak commented 1 year ago

non-repeatable simulation is another one of those in-your-face problems that you notice and fix

You'd think so, but the common construction of and care for scientific software means that that is less likely.

And if it's a multithreaded simulation, you'll want something like splittable PRNG, wouldn't you?

Sure, or just per-thread PRNGs which is why Gonum provides a collection of PRNGs and makes all rand-requiring functions accept a rand.Source rather than using the global RNG.

In most cases though simulations that scientists write will likely be single threaded and (intended to be) throwaway, these single threaded throwaway codes often become the kernel of future work and are retained by labs long after the (often student) author has left a lab. The tangle of code (often multiple programs that are loosely coupled in code but tightly couple in semantics) is rarely understood properly by the inheritors of the code.

I'm not saying any of this is good, or even marginally acceptable, but is it the reality in the house of cards that we call computational sciences and I'd really like it if it didn't get worse. The current proposal doesn't make this worse in most cases, but replacing default RNG with a CRNG would.

Merovius commented 1 year ago

@takeyourhatoff You can still do all of that by simply using rand.New(rand.NewSource(1)): https://go.dev/play/p/G4IA3tF1ryZ

The argument is exactly that that's what you should do if you require determinism anyway, as relying on the global source is brittle, Go compatibility or not. But if you want a deterministic PRNG you can still get it.