Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.Sign up
proposal: math/rand: rework for Go 2 #26263
I propose that math/rand be overhauled for Go 2.
Rob Pike has proposed some aspects of a better math/rand for Go 2 in #21835. This proposal is intended to complement and extend that proposal. It may make sense to merge them, although their scopes are (intentionally) mostly disjoint.
This proposal is not fully polished, but I have been sitting on it for months. It past time to post it. I look forward to a vigorous discussion.
A re-think of math/rand should include:
This proposal will discuss all of these, in turn, and then knit the results of those discussions into a single proposal.
Please note that I am not a subject matter expert. Input from experts (and non-experts) is welcomed.
I begin with compatibility guarantees, as decisions here impact almost every aspect of the rest of the proposal.
The Go 1 Compatibility Guarantee left some ambiguity about when the values produced math/rand should remain stable. For the life of Go 1, math/rand has remained extremely stable. To my knowledge, only a single change to the value stream has ever occurred (#16124). Many opportunities to break stream stability have been declined. There's a fair amount of discussion in #8013; it is recommended background reading. More discussion can also be found in: #13215, #14416, #11871, #8731, #12290, #6721.
There are plausible use cases in which stability matters, such as test cases discovered using testing/quick. There are also plausible use cases in which stability is irrelevant, such as adding jitter to exponential backoff code. Stability adds significant brittleness; there's not much more to math/rand beyond the exact value steam.
I propose a compromise.
Top-level convenience functions, like
This means that any given
This also means that the
Taken together, it is likely that the top level convenience functions will diverge over time from the stable
That seems more of a cmd/go question than a math/rand question.
I can see the argument for disabling caching for tests in which a random element is a key input to the test, as with testing/quick (#26276). But marking a test as not cacheable because any math/rand function has been called seems like a bad idea, since they may be called as an unrelated side-effect of what you’re actually testing, e.g. by some init function in some dependency. I’m not sure how to draw the line, but I think it’s unlikely that the line is “uses math/rand”.
In any case, I think this question is probably orthogonal to this proposal.
This is a somewhat older proposal that doesn't have much activity, but I'd still like to add one thing I've learned: there is no one-size-fits-all PRNG. The classic example is cryptographic generators, but there are occasionally subtler considerations; e.g. I've been bitten several times by using a PRNG with uniformity in too few dimensions before learning how that can matter. I believe the best approach is to provide multiple generators that target different goals, along with some elementary advice on how to choose one – which could be as simple as "try [highly uniform generator] if package-level functions or [very fast generator] produce unexpected results," or it could be a reference to a blog post with explanations and examples from experts.
Offering multiple generators also provides an opportunity for a clearer API. Rather than the name
@zephyrtronium thanks for the input. Fortunately the standard library doesn’t need to be everything to everybody. It should be a good default for most use cases. It’s ok for (say) folks doing statistical modeling to reach for something outside the standard library. (Although input from folks like @kortschak is always welcome.) And we do have crypto/rand. I’d rather err on the side of a simpler API.
It's hard to argue against a simple API. My main concern is that inappropriate choice of RNG can lead to incredibly subtle bugs, and the impact of dimensional uniformity in particular is an issue I didn't understand until I started implementing and researching PRNG algorithms, years after it affected programs I'd written. Providing multiple PRNGs would help make it clear that there are reasons to choose a particular one, which might mitigate that type of problem for others.
There are multiple choices, just not provided by the standard library. I agree with @josharian that putting additional implementations in the math/rand (or now the x/exp/rand) package is probably not a good idea; having numerous choices is as likely to result in incorrect choices when there is a need for precise choices, and more likely in the case that precise choices are not needed. Having a single PRNG that works reasonably well for most cases is I think the correct approach here. Whether there should be documentation to explicitly point the fact that other PRNGs available may be worth considering, though standard library packages generally don't name third party packages by name, and I'm not suggesting that Gonum's implementations should be noted as special.