SE-0202: Random Unification

I'm an enthusiastic supporter of this proposal — it solves a few different problems that are tricky to implement correctly on your own, especially across platforms, and does so in a way that additional functionality can be built on top.

I've updated the playground I sent out earlier to use the proposed APIs — if you're wondering about what distributions and other generators could look like in practice, please take a look! The C++ approach is pretty overwrought IMO; the proposed approach offers the same benefits while still being user-friendly when that amount of power or control isn't needed. (cc @Nevin @Jens @ahti)

Download Playground →

One of the big questions is why the numeric generating APIs are static methods rather than just using the collection-based generator or a global function. There are a few competing goals that make this an inherently tricky proposition:

  • While ranges of integers form a collection, ranges of floating-point values do not. For Double to have a consistent interface for generating random values as Int (a preferred goal of mine), we'd need manual overloads of Range<T: BinaryFloatingPoint>.random() and the same for ClosedRange.
  • While integers and even floating-point types make sense to use with just a range, for other types this pattern falls down. While not part of this proposal, extensions to other types could follow the pattern established here:
    let myVector = Vector.random(x: 20...50, y: 20...50, using: myGenerator)
    let myData = Data.random(byteCount: 400, using: myGenerator)
  • Lastly, to me at least, generating a random number and picking a random element of a collection are fundamentally different operations. The collection method must deal with emptiness to be consistent with other collection methods, but generating a number from an empty range is a programming error and should be treated as such. The overlap between these methods is unfortunate (perhaps a different name could help), but I don't think it's ruinous.