[swift-evolution] [Proposal] Random Unification

Alejandro Alonso aalonso128 at outlook.com
Thu Jan 11 21:17:27 CST 2018

Sorry it takes me forever to respond! I finally got around to writing this! School really takes all my time x)

I have a few ideas on where we can go with this:

1. I agree, we should ditch `Randomizable` as something like that should belong in a separate library or written by the user.

2. I also agree we should ditch the associated type on `RandomNumberGenerator`. I think we can sacrifice 32 bit generators using 64 bit ints to conform to the core team’s general use policy. This also makes default arguments for rngs possible which is a +1 for me!

I start to drift off from your design because `.random()` being an exception to ranges in that it’s inconsistent with other collection facilities and collection `.random()`. You make a few emails in the past that reference this as well. I think the right move is to have the static functions utilize the ranges to provide non optional types.

You wrote:

I see a couple points in favor of these static methods (or initializers) on the numeric types:

1) The collection method will need to return an optional to match the semantics of existing methods (like min()). If this is the only method available, every time someone needs a random value in the range 1...10, they’ll need to unwrap the result (with either force unwrapping, which people will complain about, or some kind of conditional binding, which is its own problem). Even if the semantics are the same (trapping on an empty range), the user experience of using a non-optional method will be better.

2) Floating-point ranges won’t get the collection method, so either we’ll have inconsistent APIs (random FP value is non-optional, random integer is optional) or we’ll make the FP API optional just to match. Both of those seem bad.

I believe this is the direction we need to go to keep consistency with collection based methods by justifying `.random(in:)` on the numeric types. With that in mind, Ben made a comment a long while ago saying, “The one downside is that you’d have to write 0..<Int.max. This might be a justification for a static property on one of the Integer protocols as shorthand for that.” I believe this makes it perfectly justifiable for `.random()` on numeric types. This also creates a consistency with `Bool.random()` making it justifiable for this as well.

We can do all of this without `Randomizable` as well! Add extension methods on `FixedWidthInteger`, `BinaryFloatingPoint`, and `Bool`. These will be the methods that crash on an empty range, but we can precondition here to provide helpful debugging for developers. You made reference to users using the range based api for a safe alternative if they needed optionals.

// Here you can use something more runtime oriented (such as an array count)
guard let x = (0 ..< 5).random() else {
 fatalError(“not going to happen")

I’m not too sure if you’ve had a change of heart, but I think the following is justifiable if we remove `Randomizable`.

Sample Syntax:
// Full Int width (shorthand for Int.random(in: .min … .max))

// random int from [0, 10)
Int.random(in: 0 ..< 10)

// random double from [0, 1) (Modulo isn’t an issue here!)

// random double from [0, .pi)
Double.random(in: 0 ..< .pi)

// random boolean

This seems very consistent to me. The only inconsistency is with `Int.random()` covering the full width, and `Double.random()` covering only `[0, 1)`, but to me this functionality is very precedented in many other languages. Also by removing `Randomizable`, other data types like `Data` don’t have to conform to a protocol, but can just add a random initializer that fits its needs.

I think now is when people will start saying, “Int.random() bad, modulo bias, no no” x)
I see the potential for error here, but logically I’m thinking that so many other languages have this feature and I wonder if you think they all did it wrong too and shouldn’t have done so. This type of behavior is found in C, C++, C#, Java, etc. I agree with Jonathon in that maybe we could suggest a warning/fixit in Xcode.

tl;dr - Remove `Randomizable`, remove associated type on `RandomNumberGenerator`, be consistent with `.random()` with other properties and functions with every collection, preserve static `random()` and `random(in:)` syntax.

- Alejandro

On Jan 8, 2018, 1:02 PM -0600, Nate Cook via swift-evolution <swift-evolution at swift.org>, wrote:
I created a playground to explore this question, starting with a minimal subset of the proposal’s additions and building from there. The attached playground demonstrates what’s possible with this subset on the first page, then uses subsequent pages to explore how the main random facilities of the C++ STL work under this model. (In my opinion, they work pretty well!)

The subset in the playground has three main differences from the proposal:
 - It doesn't include a Randomizable protocol or a random property on numeric types.
 - It doesn't include the static random(in:) methods on numeric types, either.
 - The RandomNumberGenerator protocol doesn't have an associated type. Instead, it requires all conforming types to produce UInt64 values.

I’ve tried to include a bit of real-world usage in the playground to demonstrate what writing code would look like with these additions. Please take a look!


On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution at swift.org<mailto:swift-evolution at swift.org>> wrote:

I don’t have much to say about this other than that I think the discussion seems way too narrow, focusing on spelling rather than on functionality and composability.  I consider the “generic random number library” design to be a mostly-solved problem, in the C++ standard library (http://en.cppreference.com/w/cpp/numeric/random).  Whatever goes into the Swift standard library does not need to have all those features right away, but should support being extended into something having the same general shape. IMO the right design strategy is to implement and use a Swift version of C++’s facilities and only then consider proposing [perhaps a subset of] that design for standardization in Swift.

Sent from my iPad

On Dec 2, 2017, at 5:12 PM, Kyle Murray via swift-evolution <swift-evolution at swift.org<mailto:swift-evolution at swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution at swift.org<mailto:swift-evolution at swift.org>> wrote:

Instead, we ought to make clear to users both the features and the limitations of this API, to encourage use where suitable and to discourage use where unsuitable.

I like that you're considering the balance here. I've been lightly following this thread and want to add my thoughts on keeping crypto and pseudorandomness out of the name of at least one random API intended for general use.

For someone who doesn't know or care about the subtleties of insecure or pseudorandom numbers, I'm not sure that the name insecureRandom is effectively much different than badRandom, at least in terms of the information it conveys to non-experts. To Greg's point, that's the opposite of the signal that the API name should suggest because it's what most people should use most of the time. As you say, this API is being designed for general use.

There's a cost to adding extra complexity to names, too. I don't think it's far-fetched to suspect that people who find insecureRandom in an autocomplete listing or search will think "Where's the plain random function?"... and then go looking for a community extension that will inevitably provide a trivial alias: func random() { return insecureRandom() }. That's the sort of adoption I'd expect from something for new programmers, like Swift Playgrounds. Someone's introduction to randomness in programming should probably involve no more than a straightforward mapping from the elementary definition, rather than forcing a teaching moment from more advanced math.

I think there are better places for caveat information than in the API names themselves; documentation being one clear destination. This is in contrast with Unsafe*Pointer, where the safety element is critical enough to be elevated to be more than caveat-level information. You can go really far and create really cool things before these caveats start to apply. Using randomness as a black box in an intro programming environment seems like a much more common scenario than someone attempting to roll their first crypto by only reading API names and hoping for the best.

swift-evolution mailing list
swift-evolution at swift.org<mailto:swift-evolution at swift.org>
swift-evolution mailing list
swift-evolution at swift.org<mailto:swift-evolution at swift.org>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20180112/b9cbd201/attachment.html>

More information about the swift-evolution mailing list