[Proposal] Random Unification

+1 to this design

Just my 2 cents but this looks very simple and easy to use due to being very consistent. I also like that Randomizable was dropped as I feel it doesn’t hold its weight.

···

On 08 Jan 2018, at 21:02, Nate Cook via swift-evolution <swift-evolution@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!

Nate

Some thoughts:

- How do I randomly select an enum?

- I like that RandomNumberGenerator doesn’t have an associated type. I agree that we should just spit out UInt64s for simplicity.

- I don’t like how it is so closely tied with Range. I realize that both Int and Float work with Ranges, but other random types do not (e.g. CGVectors). You are special casing FixedWidthInteger and BinaryFloatingPoint, which are very important… but we lose the ability to deal with other randomly generated types.

- Following on the previous point, I don’t like that the code for dealing with Integers/Floats is in Range. It feels like things aren’t properly encapsulated.

- Why bother supporting non-closed Ranges at all? If you only allow closed ranges, then you can’t end up with an empty range. The only difference in behavior I can think of is on floating point, but I can’t think of a use-case where excluding the supremum is actually useful in any real world way.

- This may sound strange, but I would really like to see Bool handled as a default implementation on the generator protocol itself. On my own version of this I have both the ‘coinFlip()’ and ‘oneIn(_ num:Int)’ methods which I find extremely useful. CoinFlip just gives you a random bool, whereas you can say things like oneIn(100) to get ‘true’ roughly 1 out of every 100 times you call it. These are useful for branching randomly. They are most useful on the source/generator itself because it is ergonomic when you need to rewind the source.

- IMO distributions should be sources/generators themselves which just wrap another source. We could have a subprotocol of RandomNumberGenerator which just semantically guarantees uniform distribution, and then distributions that need it could be sure of the input distribution. Notice this doesn’t limit the distribution to only be used for Integers as they are in the demo. They can be used anywhere a source can be used.

- Having a subprotocol for generators which can be rewound is extremely important for entire classes of real-world problems. I have spent a lot of time using this and it solves a LOT of problems. For example, I have a Lorem Ipsum Generator which takes Attributes and a CGSize to fill. It works by branching (using the Bool methods above) and then rewinding bits which don’t fit (If you just futz with the last part instead of generating appropriate clauses, it won’t look right). I also have a bunch of backtracking algorithms which rely on this rewind ability. Plus numerous visual effects which rely on a repeatable rewindable source.
  - Tl;dr: It isn’t enough to just have a seed, you need to be able to mark a state of a generator and return to that state later.

  My RepeatableRandomSource Protocol has 3 extra methods:
  - It takes a seed
  - It has a mark() method which returns a token
  - It has a returnToMark(_ mark:Mark) method which takes a token and restores the appropriate state

- I really appreciate that you made a playground :-)

Thanks,
Jon

···

On Jan 8, 2018, at 11:02 AM, Nate Cook via swift-evolution <swift-evolution@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!

Nate

<Random.playground.zip>

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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))
Int.random()

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

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

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

// random boolean
Bool.random()

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@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!

Nate

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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@swift.org<mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Oh OK, I must have misunderstood this thread:

[swift-evolution] Core Team vs Random number API discussion
(
"The strong opinion of the core team is that such an API should *not* be
designed with an attempt to service people writing crypto code."
"It is general goodness if generality for crypto use cases somehow falls
out of the design. However, the design for general use shouldn’t suffer
because of that goal."
)

I assumed that the Random API would save the user from the trouble of
making a good choice and implementation (fast and good quality) of a
"standard" general purpose prng (as well as maybe a cryptographically
secure one).

Providing a cryptographically secure PRNG is necessary but not sufficient
for cryptography. However, many ordinary uses of a general purpose PRNG
will assume that future “random” numbers cannot be guessed by observing a
small number of previous ones. Xoroshiro has its uses, but it would not be
the ideal basis for a general purpose PRNG, especially since we have easy
access to true randomness.

Also, the most commonly recommended ways of converting from eg 64 random

bits to an int range or a floating point range are unnecessarily bad and
slow, so I figured the webpage was worth a read, in addition to C++
stdlib's implementation.

I agree it will be critical to ensure that random floating-point values are
truly uniform. All implementors would do well to study the state of the art.

···

On Wed, Dec 20, 2017 at 13:13 Jens Persson <jens@bitcycle.com> wrote:

/Jens

On Wed, Dec 20, 2017 at 4:55 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

xoroshiro128+ is not a cryptographically secure algorithm and would not
be incorporated into the Random API, though it is trivial to implement your
own; the proposal outlines sources of randomness that are cryptographically
secure.

On Wed, Dec 20, 2017 at 09:46 Jens Persson via swift-evolution < >> swift-evolution@swift.org> wrote:

I'd like to add a pointer to the information here:
http://xoroshiro.di.unimi.it

since AFAICS, the xoroshiro128+ generator and the method of "Generating
uniform doubles in the unit interval" should probably be implemented in any
modern general purpose Random API.

Please correct me if there are more up to date (higher quality and
faster) general purpose generators and ways of converting UInt64 bit
patterns to floating point [0, 1).

/Jens

On Sun, Dec 3, 2017 at 4:50 AM, Dave Abrahams via swift-evolution < >>> swift-evolution@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@swift.org> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution < >>>> swift-evolution@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.

-Kyle

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I made changes last night that uses methods rather than properties if that’s all you’re worried about.

···

Sent from my iPhone

On Jan 8, 2018, at 15:27, David Hart via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>> wrote:

I much prefer the API from Nate Cook compared to the previous proposal. Its simpler, while still very powerful, and closer to Swift conventions (method instead of property).

On 8 Jan 2018, at 20:02, Nate Cook via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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!

Nate

<Random.playground.zip>

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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@swift.org<mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Unfortunately, it wasn’t my only worry. The simplifications from dropping Randomizable and random(in: ) are also very welcome for me.

···

On 8 Jan 2018, at 23:08, Alejandro Alonso <aalonso128@outlook.com> wrote:

I made changes last night that uses methods rather than properties if that’s all you’re worried about.

Sent from my iPhone

On Jan 8, 2018, at 15:27, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I much prefer the API from Nate Cook compared to the previous proposal. Its simpler, while still very powerful, and closer to Swift conventions (method instead of property).

On 8 Jan 2018, at 20:02, Nate Cook via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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!

Nate

<Random.playground.zip>

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Some thoughts:

- How do I randomly select an enum?

Well… combine this with SE-0194 and you have all the basics you need: the set of all enum values in a collection, and a way to pick a random element from a collection...

···

On Jan 9, 2018, at 3:12 AM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

- I like that RandomNumberGenerator doesn’t have an associated type. I agree that we should just spit out UInt64s for simplicity.

- I don’t like how it is so closely tied with Range. I realize that both Int and Float work with Ranges, but other random types do not (e.g. CGVectors). You are special casing FixedWidthInteger and BinaryFloatingPoint, which are very important… but we lose the ability to deal with other randomly generated types.

- Following on the previous point, I don’t like that the code for dealing with Integers/Floats is in Range. It feels like things aren’t properly encapsulated.

- Why bother supporting non-closed Ranges at all? If you only allow closed ranges, then you can’t end up with an empty range. The only difference in behavior I can think of is on floating point, but I can’t think of a use-case where excluding the supremum is actually useful in any real world way.

- This may sound strange, but I would really like to see Bool handled as a default implementation on the generator protocol itself. On my own version of this I have both the ‘coinFlip()’ and ‘oneIn(_ num:Int)’ methods which I find extremely useful. CoinFlip just gives you a random bool, whereas you can say things like oneIn(100) to get ‘true’ roughly 1 out of every 100 times you call it. These are useful for branching randomly. They are most useful on the source/generator itself because it is ergonomic when you need to rewind the source.

- IMO distributions should be sources/generators themselves which just wrap another source. We could have a subprotocol of RandomNumberGenerator which just semantically guarantees uniform distribution, and then distributions that need it could be sure of the input distribution. Notice this doesn’t limit the distribution to only be used for Integers as they are in the demo. They can be used anywhere a source can be used.

- Having a subprotocol for generators which can be rewound is extremely important for entire classes of real-world problems. I have spent a lot of time using this and it solves a LOT of problems. For example, I have a Lorem Ipsum Generator which takes Attributes and a CGSize to fill. It works by branching (using the Bool methods above) and then rewinding bits which don’t fit (If you just futz with the last part instead of generating appropriate clauses, it won’t look right). I also have a bunch of backtracking algorithms which rely on this rewind ability. Plus numerous visual effects which rely on a repeatable rewindable source.
  - Tl;dr: It isn’t enough to just have a seed, you need to be able to mark a state of a generator and return to that state later.

  My RepeatableRandomSource Protocol has 3 extra methods:
  - It takes a seed
  - It has a mark() method which returns a token
  - It has a returnToMark(_ mark:Mark) method which takes a token and restores the appropriate state

- I really appreciate that you made a playground :-)

Thanks,
Jon

On Jan 8, 2018, at 11:02 AM, Nate Cook via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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!

Nate

<Random.playground.zip>

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Some thoughts:

- How do I randomly select an enum?

Vote for SE-0194! :)

- I like that RandomNumberGenerator doesn’t have an associated type. I agree that we should just spit out UInt64s for simplicity.

It simplifies things a lot, at some performance cost. For example, the LCRNG type really produces UInt32s, so I have to use two of its outputs to generate one value, even if I don’t need that many bits.

- I don’t like how it is so closely tied with Range. I realize that both Int and Float work with Ranges, but other random types do not (e.g. CGVectors). You are special casing FixedWidthInteger and BinaryFloatingPoint, which are very important… but we lose the ability to deal with other randomly generated types.

- Following on the previous point, I don’t like that the code for dealing with Integers/Floats is in Range. It feels like things aren’t properly encapsulated.

I actually agree with you, and for getting individual values prefer the form `let x = Int.random(in: 1…10)`. Here’s how I got to what’s in the playground:

1) We definitely want to be able to select a random element from a collection.
2) Given that, we’ll have (1…10).random() and (0..<10).random() even if those aren’t what we prefer, and people will use them.
3) If people use that construction for integers, it will be strange to not have the same facility for floating-point numbers.
4) Once we have the range-based capability for both, the type-based versions are redundant (i.e., they can be added in the future if we decide we made the wrong decision by excluding them).

You’re of course correct that a pattern of range-based random functions doesn’t extend well to other types. I show on the last page a couple different ways of writing those, for Bool and Data. Most of the other types you’d want to create lie outside the Swift standard library, so we can’t address really those here.

- Why bother supporting non-closed Ranges at all? If you only allow closed ranges, then you can’t end up with an empty range. The only difference in behavior I can think of is on floating point, but I can’t think of a use-case where excluding the supremum is actually useful in any real world way.

Half-open ranges are a major use case for generating random numbers, particularly when working with collections. Whenever you see that someone’s written `random() % n`, that’s the half-open range 0..<n.

- This may sound strange, but I would really like to see Bool handled as a default implementation on the generator protocol itself. On my own version of this I have both the ‘coinFlip()’ and ‘oneIn(_ num:Int)’ methods which I find extremely useful. CoinFlip just gives you a random bool, whereas you can say things like oneIn(100) to get ‘true’ roughly 1 out of every 100 times you call it. These are useful for branching randomly. They are most useful on the source/generator itself because it is ergonomic when you need to rewind the source.

Bool is certainly a very important type to be able to randomly generate. I’m not opposed to it being included in a proposal, but it’s simple enough to do on your own that it didn’t pass the “minimal” test that I was using in the playground. You could use something like this static method:

    extension Bool {
        static func random(
            probability: Double = 0.5,
            using generator: RandomNumberGenerator = Random.default
        ) {
            return (0.0 ..< 1.0).random(using: generator) < probability
        }
    }

I don’t think there should be any value-producing methods on generators—most users shouldn’t need to think about generators at all, and the ones who have a specific need (repeatability, rewinding, etc) should be able to use the same APIs as the people who aren’t thinking about them.

- IMO distributions should be sources/generators themselves which just wrap another source. We could have a subprotocol of RandomNumberGenerator which just semantically guarantees uniform distribution, and then distributions that need it could be sure of the input distribution. Notice this doesn’t limit the distribution to only be used for Integers as they are in the demo. They can be used anywhere a source can be used.

I’d really like to maintain a clear line between generators and distributions (which I don’t think we need an additional protocol for). Distributions create values of a specific kind of type, with a particular distribution, which usually isn't suitable to use as the input for another algorithm that needs random data. Generators just pump out (hopefully) uniformly distributed bits, which distributions and other algorithms can then interpret and shape.

- Having a subprotocol for generators which can be rewound is extremely important for entire classes of real-world problems. I have spent a lot of time using this and it solves a LOT of problems. For example, I have a Lorem Ipsum Generator which takes Attributes and a CGSize to fill. It works by branching (using the Bool methods above) and then rewinding bits which don’t fit (If you just futz with the last part instead of generating appropriate clauses, it won’t look right). I also have a bunch of backtracking algorithms which rely on this rewind ability. Plus numerous visual effects which rely on a repeatable rewindable source.
  - Tl;dr: It isn’t enough to just have a seed, you need to be able to mark a state of a generator and return to that state later.

  My RepeatableRandomSource Protocol has 3 extra methods:
  - It takes a seed
  - It has a mark() method which returns a token
  - It has a returnToMark(_ mark:Mark) method which takes a token and restores the appropriate state

That’s neat, I’ve never used a generator like this! The RandomNumberGenerator protocol would certainly allow for a generator with these features, and libraries or future additions to the standard library could include that kind of protocol.

- I really appreciate that you made a playground :-)

Thanks!

Nate

···

On Jan 9, 2018, at 4:12 AM, Jonathan Hull <jhull@gbis.com> wrote:

Thanks,
Jon

On Jan 8, 2018, at 11:02 AM, Nate Cook via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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!

Nate

<Random.playground.zip>

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Ranges are the currency type, whereas closed ranges aren’t. We should try to avoid any solution that goes against this pattern. People are going to have a Range, and having to convert it into a ClosedRange just to get a random number from it is confusing.

The argument goes that you want to avoid traps, therefore forbid half-open range because it can be empty and might trap, whereas closed ranges doesn’t. Therefore, let’s only have closed ranges. Type safety ftw.

In practice, I don’t think this is justified. Realistically, you can divide uses into two cases, literals and runtime-generated ranges.

Literals are obviously empty by inspection. It’s hard to do this by accident and any kind of coverage testing of (0..<0).random() will immediately trap. So probably a non-issue.

If you’re generating ranges at runtime from variables, you have another risk of traps that applies just as much to closed ranges: inversion.

i.e.:

x = 5
y = 4
x...y // boom, can't form Range with upperBound < lowerBound

This is easily done. Nate’s example playground even had a possible case!

// better hope items always has at least 3 elements...
let countForSale = (3...items.count).random()

Given this is already an issue, the additional risk of trapping on empty half-open ranges seems modest and acceptable to me, compared to the alternative of encouraging constant banging of the result from .random() on ranges.

···

On Jan 9, 2018, at 2:12 AM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

- Why bother supporting non-closed Ranges at all? If you only allow closed ranges, then you can’t end up with an empty range. The only difference in behavior I can think of is on floating point, but I can’t think of a use-case where excluding the supremum is actually useful in any real world way.

Some thoughts:

- How do I randomly select an enum?

- I like that RandomNumberGenerator doesn’t have an associated type. I
agree that we should just spit out UInt64s for simplicity.

- I don’t like how it is so closely tied with Range. I realize that both
Int and Float work with Ranges, but other random types do not (e.g.
CGVectors). You are special casing FixedWidthInteger and
BinaryFloatingPoint, which are very important… but we lose the ability to
deal with other randomly generated types.

So, I’ll defend this design. At some point, you need to expose the
primitive operation, which is the selection of a random *number* within a
range, and after our very lengthy discussions, this is the best spelling in
Swift by consensus.

- Following on the previous point, I don’t like that the code for dealing

with Integers/Floats is in Range. It feels like things aren’t properly
encapsulated.

- Why bother supporting non-closed Ranges at all? If you only allow
closed ranges, then you can’t end up with an empty range. The only
difference in behavior I can think of is on floating point, but I can’t
think of a use-case where excluding the supremum is actually useful in any
real world way.

This is a nontrivial thing to get right; in fact, in C++, many
implementations of the standard library got it wrong for quite some time.
Additionally, generating a value in 0..<1 is a primitive operation which is
equivalent, essentially, to generating a random significand, so it is
important to expose this functionality in the standard library.

- This may sound strange, but I would *really* like to see Bool handled as

a default implementation on the generator protocol itself. On my own
version of this I have both the ‘coinFlip()’ and ‘oneIn(_ num:Int)’ methods
which I find extremely useful. CoinFlip just gives you a random bool,
whereas you can say things like oneIn(100) to get ‘true’ roughly 1 out of
every 100 times you call it. These are useful for branching randomly.
They are most useful on the source/generator itself because it is ergonomic
when you need to rewind the source.

This is a Bernoulli distribution. Which is a trivial distribution to
implement—if you have a primitive that gives you a random value in the
range 0..<1! (See, this is why you need that as a primitive.) Then, for
example, a fair coin is obtained by binning 0..<0.5 as heads and 0.5..<1 as
tails. Adjust to taste for your desired value of p.

However, more below on distributions.

- IMO distributions should be sources/generators themselves which just wrap

another source. We could have a subprotocol of RandomNumberGenerator which
just semantically guarantees uniform distribution, and then distributions
that need it could be sure of the input distribution. Notice this doesn’t
limit the distribution to only be used for Integers as they are in the
demo. They can be used anywhere a source can be used.

One of the major critiques of the C++ design is its overcomplicated design,
one aspect of which is this wrapping of sources. Some people might like it
or find it to be their preferred design, but given that there’s some
consensus that the uniform distribution is all we’re offering in the
standard library (with everything else being more appropriate for a
third-party library that’s more elaborate), this is a design decision that
each such third-party library can decide for itself.

- Having a subprotocol for generators which can be rewound is extremely

important for entire classes of real-world problems. I have spent a lot of
time using this and it solves a LOT of problems. For example, I have a
Lorem Ipsum Generator which takes Attributes and a CGSize to fill. It
works by branching (using the Bool methods above) and then rewinding bits
which don’t fit (If you just futz with the last part instead of generating
appropriate clauses, it won’t look right). I also have a bunch of
backtracking algorithms which rely on this rewind ability. Plus numerous
visual effects which rely on a repeatable rewindable source.
*- Tl;dr: It isn’t enough to just have a seed, you need to be able to mark
a state of a generator and return to that state later.*

My RepeatableRandomSource Protocol has 3 extra methods:
- It takes a seed
- It has a mark() method which returns a token
- It has a returnToMark(_ mark:Mark) method which takes a token and
restores the appropriate state

This too is one of those features that, given how we’re not offering any
such RNGs in the standard library, can be a decision for custom libraries
to design in a way that most suits themselves. Many RNGs can be seeded and
reseeded, but not all can be rewound or skipped ahead, so it’s something
where the ultimate hierarchy of protocols and their methods will depend on
the concrete types on offer.

- I really appreciate that you made a playground :-)

···

On Tue, Jan 9, 2018 at 05:12 Jonathan Hull via swift-evolution < swift-evolution@swift.org> wrote:

Thanks,
Jon

On Jan 8, 2018, at 11:02 AM, Nate Cook via swift-evolution < > swift-evolution@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!

Nate

<Random.playground.zip>

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution < > swift-evolution@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@swift.org> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution < > swift-evolution@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.

-Kyle

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Some thoughts:

- How do I randomly select an enum?

carefully, of course

- I like that RandomNumberGenerator doesn’t have an associated type. I
agree that we should just spit out UInt64s for simplicity.

- I don’t like how it is so closely tied with Range. I realize that both
Int and Float work with Ranges, but other random types do not (e.g.
CGVectors). You are special casing FixedWidthInteger and
BinaryFloatingPoint, which are very important… but we lose the ability to
deal with other randomly generated types.

generating “random vectors” is a *big* mood and it’s not as simple as
varying on a range of *R*3. There’s a mild but non-trivial amount of work
needed to get a uniform distribution and avoid degeneracies and division by
zero. and “random” can mean different things, sometimes you want a random
2D disk of vectors that all live in some plane in 3D space because you need
them to be perpendicular to something. And at any rate, CGVector is not a
standard type, while Int and Float are.

- Following on the previous point, I don’t like that the code for dealing
with Integers/Floats is in Range. It feels like things aren’t properly
encapsulated.

- Why bother supporting non-closed Ranges at all? If you only allow
closed ranges, then you can’t end up with an empty range. The only
difference in behavior I can think of is on floating point, but I can’t
think of a use-case where excluding the supremum is actually useful in any
real world way.

i see this as an inconvenience for the sake of security theater. Usually
when you want a random integer on a continuous range, you’re using them as
indices into something. And indices go from 0 ..< count, not 0 ... (count -
1). if whatever you’re indexing into is empty, it’s empty.

···

On Tue, Jan 9, 2018 at 5:12 AM, Jonathan Hull via swift-evolution < swift-evolution@swift.org> wrote:

- This may sound strange, but I would *really* like to see Bool handled
as a default implementation on the generator protocol itself. On my own
version of this I have both the ‘coinFlip()’ and ‘oneIn(_ num:Int)’ methods
which I find extremely useful. CoinFlip just gives you a random bool,
whereas you can say things like oneIn(100) to get ‘true’ roughly 1 out of
every 100 times you call it. These are useful for branching randomly.
They are most useful on the source/generator itself because it is ergonomic
when you need to rewind the source.

- IMO distributions should be sources/generators themselves which just
wrap another source. We could have a subprotocol of RandomNumberGenerator
which just semantically guarantees uniform distribution, and then
distributions that need it could be sure of the input distribution. Notice
this doesn’t limit the distribution to only be used for Integers as they
are in the demo. They can be used anywhere a source can be used.

- Having a subprotocol for generators which can be rewound is extremely
important for entire classes of real-world problems. I have spent a lot of
time using this and it solves a LOT of problems. For example, I have a
Lorem Ipsum Generator which takes Attributes and a CGSize to fill. It
works by branching (using the Bool methods above) and then rewinding bits
which don’t fit (If you just futz with the last part instead of generating
appropriate clauses, it won’t look right). I also have a bunch of
backtracking algorithms which rely on this rewind ability. Plus numerous
visual effects which rely on a repeatable rewindable source.
*- Tl;dr: It isn’t enough to just have a seed, you need to be able to mark
a state of a generator and return to that state later.*

My RepeatableRandomSource Protocol has 3 extra methods:
- It takes a seed
- It has a mark() method which returns a token
- It has a returnToMark(_ mark:Mark) method which takes a token and
restores the appropriate state

- I really appreciate that you made a playground :-)

Thanks,
Jon

On Jan 8, 2018, at 11:02 AM, Nate Cook via swift-evolution < > swift-evolution@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!

Nate

<Random.playground.zip>

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution < > swift-evolution@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@swift.org> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution < > swift-evolution@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.

-Kyle

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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))
Int.random()

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

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

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

// random boolean
Bool.random()

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.

You will have noted, surely, the evidence given above that in Apple's
internal code the majority of uses of APIs equivalent to `Int.random()` are
erroneous due to unintended modulo bias. Yes, what that would imply is that
all the other languages did it wrong. Why do you feel it is so important to
have a shorthand that enables no additional behavior in the face of
overwhelming evidence that it is actively harmful?

Likewise, you acknowledge the inconsistency of Int.random() and
Double.random(), yet again suggest a shorthand that enables no additional
behavior in spite of the potential for misunderstanding. What advantage of
having such an API do you see which could overcome this very glaring
drawback?

This type of behavior is found in C, C++, C#, Java, etc. I agree with

···

On Thu, Jan 11, 2018 at 9:17 PM, Alejandro Alonso via swift-evolution < swift-evolution@swift.org> wrote:

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@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!

Nate

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution < > swift-evolution@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@swift.org> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution < > swift-evolution@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.

-Kyle

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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))
Int.random()

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

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

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

// random boolean
Bool.random()

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.

I would co-sign all of this except for preserving Int.random() and Double.random(), for the reasons I’ve laid out before, those Xiaodi responded with, and even the ergonomics of writing and reading code:

- For floating-point values, if someone wants a [0, 1) range, I see no problem with having them write `Double.random(in: 0..<1)`. That’s still nice and short, and there’s no question about what kind of values are produced.

- For integers, I see a basic breakdown into two use cases. First, there’s the case where you need a number in a specific range, for which the range-based methods are perfect. When you instead need a full-width integer, you’re generally using that integer as data, rather than as a value. The signedness of `Int` is a pain in that case, so you could either write `UInt64.random(in: .min ... .max)` or else access data from whatever generator you’re using—`Random.default.next()`.

Also, from a discoverability and documentation perspective, I like having static `random` methods on the types that they generate. That feels like an easier pattern for a developer to recognize and use.

Nate

···

On Jan 11, 2018, at 9:17 PM, Alejandro Alonso <aalonso128@outlook.com> wrote:

- Alejandro

On Jan 8, 2018, 1:02 PM -0600, Nate Cook via swift-evolution <swift-evolution@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!

Nate

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

I am willing to give `.random()`. However I still wish to preserve `.random(in:)` and `.random()` on Bool.

···

Sent from my iPhone

On Jan 11, 2018, at 21:28, Xiaodi Wu <xiaodi.wu@gmail.com<mailto:xiaodi.wu@gmail.com>> wrote:

On Thu, Jan 11, 2018 at 9:17 PM, Alejandro Alonso via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>> wrote:
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))
Int.random()

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

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

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

// random boolean
Bool.random()

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.

You will have noted, surely, the evidence given above that in Apple's internal code the majority of uses of APIs equivalent to `Int.random()` are erroneous due to unintended modulo bias. Yes, what that would imply is that all the other languages did it wrong. Why do you feel it is so important to have a shorthand that enables no additional behavior in the face of overwhelming evidence that it is actively harmful?

Likewise, you acknowledge the inconsistency of Int.random() and Double.random(), yet again suggest a shorthand that enables no additional behavior in spite of the potential for misunderstanding. What advantage of having such an API do you see which could overcome this very glaring drawback?

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@swift.org<mailto:swift-evolution@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!

Nate

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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@swift.org<mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

We are doing crazy contortions to avoid the 'random % 100’ issue. Why not just check for that pattern and issue a warning with a fixit to do it better?

I don’t think it is worth handicapping everything just to avoid this.

Thanks,
Jon

···

On Jan 11, 2018, at 11:22 PM, Nate Cook via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 11, 2018, at 9:17 PM, Alejandro Alonso <aalonso128@outlook.com <mailto:aalonso128@outlook.com>> wrote:

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))
Int.random()

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

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

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

// random boolean
Bool.random()

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.

I would co-sign all of this except for preserving Int.random() and Double.random(), for the reasons I’ve laid out before, those Xiaodi responded with, and even the ergonomics of writing and reading code:

- For floating-point values, if someone wants a [0, 1) range, I see no problem with having them write `Double.random(in: 0..<1)`. That’s still nice and short, and there’s no question about what kind of values are produced.

- For integers, I see a basic breakdown into two use cases. First, there’s the case where you need a number in a specific range, for which the range-based methods are perfect. When you instead need a full-width integer, you’re generally using that integer as data, rather than as a value. The signedness of `Int` is a pain in that case, so you could either write `UInt64.random(in: .min ... .max)` or else access data from whatever generator you’re using—`Random.default.next()`.

Also, from a discoverability and documentation perspective, I like having static `random` methods on the types that they generate. That feels like an easier pattern for a developer to recognize and use.

Nate

- Alejandro

On Jan 8, 2018, 1:02 PM -0600, Nate Cook via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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!

Nate

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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 nameinsecureRandom 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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Done and done!

···

Sent from my iPhone

On Jan 12, 2018, at 01:22, Nate Cook <natecook@apple.com<mailto:natecook@apple.com>> wrote:

On Jan 11, 2018, at 9:17 PM, Alejandro Alonso <aalonso128@outlook.com<mailto:aalonso128@outlook.com>> wrote:

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))
Int.random()

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

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

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

// random boolean
Bool.random()

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.

I would co-sign all of this except for preserving Int.random() and Double.random(), for the reasons I’ve laid out before, those Xiaodi responded with, and even the ergonomics of writing and reading code:

- For floating-point values, if someone wants a [0, 1) range, I see no problem with having them write `Double.random(in: 0..<1)`. That’s still nice and short, and there’s no question about what kind of values are produced.

- For integers, I see a basic breakdown into two use cases. First, there’s the case where you need a number in a specific range, for which the range-based methods are perfect. When you instead need a full-width integer, you’re generally using that integer as data, rather than as a value. The signedness of `Int` is a pain in that case, so you could either write `UInt64.random(in: .min ... .max)` or else access data from whatever generator you’re using—`Random.default.next()`.

Also, from a discoverability and documentation perspective, I like having static `random` methods on the types that they generate. That feels like an easier pattern for a developer to recognize and use.

Nate

- Alejandro

On Jan 8, 2018, 1:02 PM -0600, Nate Cook via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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!

Nate

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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@swift.org<mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Agreed. This simplification is more or less precisely the shape of the
proposal that I was hoping for.

I'm worried that "optional" `random()` and "non-optional" `random()` are
spelled the same way, especially as the type is not always obvious when,
say, a range or collection is referred to by variable name and not as a
literal. And I do think that may become even more problematic should we
decide that some ranges (i.e., the countable ones) ought to conform to
Collection. (But more on that later.)

But besides that concern (which, I would hope, should not be glossed over),
I do like the direction of Nate's playground.

···

On Mon, Jan 8, 2018 at 5:37 PM, David Hart via swift-evolution < swift-evolution@swift.org> wrote:

Unfortunately, it wasn’t my only worry. The simplifications from dropping
Randomizable and random(in: ) are also very welcome for me.

On 8 Jan 2018, at 23:08, Alejandro Alonso <aalonso128@outlook.com> wrote:

I made changes last night that uses methods rather than properties if
that’s all you’re worried about.

Sent from my iPhone

On Jan 8, 2018, at 15:27, David Hart via swift-evolution < > swift-evolution@swift.org> wrote:

I much prefer the API from Nate Cook compared to the previous proposal.
Its simpler, while still very powerful, and closer to Swift conventions
(method instead of property).

On 8 Jan 2018, at 20:02, Nate Cook via swift-evolution < > swift-evolution@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!

Nate

<Random.playground.zip>

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution < > swift-evolution@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@swift.org> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution < > swift-evolution@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.

-Kyle

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

+1

···

On Jan 9, 2018, at 11:01 AM, Ben Cohen via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 9, 2018, at 2:12 AM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

- Why bother supporting non-closed Ranges at all? If you only allow closed ranges, then you can’t end up with an empty range. The only difference in behavior I can think of is on floating point, but I can’t think of a use-case where excluding the supremum is actually useful in any real world way.

Ranges are the currency type, whereas closed ranges aren’t. We should try to avoid any solution that goes against this pattern. People are going to have a Range, and having to convert it into a ClosedRange just to get a random number from it is confusing.

The argument goes that you want to avoid traps, therefore forbid half-open range because it can be empty and might trap, whereas closed ranges doesn’t. Therefore, let’s only have closed ranges. Type safety ftw.

In practice, I don’t think this is justified. Realistically, you can divide uses into two cases, literals and runtime-generated ranges.

Literals are obviously empty by inspection. It’s hard to do this by accident and any kind of coverage testing of (0..<0).random() will immediately trap. So probably a non-issue.

If you’re generating ranges at runtime from variables, you have another risk of traps that applies just as much to closed ranges: inversion.

i.e.:

x = 5
y = 4
x...y // boom, can't form Range with upperBound < lowerBound

This is easily done. Nate’s example playground even had a possible case!

// better hope items always has at least 3 elements...
let countForSale = (3...items.count).random()

Given this is already an issue, the additional risk of trapping on empty half-open ranges seems modest and acceptable to me, compared to the alternative of encouraging constant banging of the result from .random() on ranges.

Some thoughts:

- How do I randomly select an enum?

Vote for SE-0194! :)

- I like that RandomNumberGenerator doesn’t have an associated type. I agree that we should just spit out UInt64s for simplicity.

It simplifies things a lot, at some performance cost. For example, the LCRNG type really produces UInt32s, so I have to use two of its outputs to generate one value, even if I don’t need that many bits.

I agree with you, but just to play devil’s advocate, why not have two outputs: one for UInt32 and another for UInt64? The generator itself would only have to provide one of the UInt types and the other would be provided by default implementation (either by stacking 2 UInt32s or discarding half of a UInt64). I know this is less minimal, but I don’t think it is actually complicated or hard to use. You just ask for the type that is most useful for you.

- I don’t like how it is so closely tied with Range. I realize that both Int and Float work with Ranges, but other random types do not (e.g. CGVectors). You are special casing FixedWidthInteger and BinaryFloatingPoint, which are very important… but we lose the ability to deal with other randomly generated types.

- Following on the previous point, I don’t like that the code for dealing with Integers/Floats is in Range. It feels like things aren’t properly encapsulated.

I actually agree with you, and for getting individual values prefer the form `let x = Int.random(in: 1…10)`. Here’s how I got to what’s in the playground:

1) We definitely want to be able to select a random element from a collection.
2) Given that, we’ll have (1…10).random() and (0..<10).random() even if those aren’t what we prefer, and people will use them.
3) If people use that construction for integers, it will be strange to not have the same facility for floating-point numbers.
4) Once we have the range-based capability for both, the type-based versions are redundant (i.e., they can be added in the future if we decide we made the wrong decision by excluding them).

You’re of course correct that a pattern of range-based random functions doesn’t extend well to other types. I show on the last page a couple different ways of writing those, for Bool and Data. Most of the other types you’d want to create lie outside the Swift standard library, so we can’t address really those here.

Right. I guess my thought is that I would like them to be able to use a standard creation pattern so it doesn’t vary from type to type (that is the whole point of “unification” in my mind). In my own code, I have a concept of constraint, of which a set are passed to the object being created. This allows me to random create colors which look good together, etc…. I then have some convenience methods which just automatically create an appropriate constraint from a range where appropriate. I’d really like to see something standard which allows for constraints other than simple ranges.

I think I would feel at least a little better if the range stuff worked for any type which can be put in a range…

I also feel like the focus on ranges to the exclusion of everything else is one of those cute things that will come back to bite us later. My main focus is on random things which are presented to the user in some way, and I realize that is a different use-case than most programmers. Even for things like pure number generation, I am worried about things like sig-figs and not just range.

- Why bother supporting non-closed Ranges at all? If you only allow closed ranges, then you can’t end up with an empty range. The only difference in behavior I can think of is on floating point, but I can’t think of a use-case where excluding the supremum is actually useful in any real world way.

Half-open ranges are a major use case for generating random numbers, particularly when working with collections. Whenever you see that someone’s written `random() % n`, that’s the half-open range 0..<n.

Ok, you convinced me on the half-open ranges.

I still think the best way to handle the 'random() % n' issue is to look for it as a pattern and then just issue a warning/fixit.

- This may sound strange, but I would really like to see Bool handled as a default implementation on the generator protocol itself. On my own version of this I have both the ‘coinFlip()’ and ‘oneIn(_ num:Int)’ methods which I find extremely useful. CoinFlip just gives you a random bool, whereas you can say things like oneIn(100) to get ‘true’ roughly 1 out of every 100 times you call it. These are useful for branching randomly. They are most useful on the source/generator itself because it is ergonomic when you need to rewind the source.

Bool is certainly a very important type to be able to randomly generate. I’m not opposed to it being included in a proposal, but it’s simple enough to do on your own that it didn’t pass the “minimal” test that I was using in the playground. You could use something like this static method:

    extension Bool {
        static func random(
            probability: Double = 0.5,
            using generator: RandomNumberGenerator = Random.default
        ) {
            return (0.0 ..< 1.0).random(using: generator) < probability
        }
    }

I don’t think there should be any value-producing methods on generators—most users shouldn’t need to think about generators at all, and the ones who have a specific need (repeatability, rewinding, etc) should be able to use the same APIs as the people who aren’t thinking about them.

I can add them easily myself. That said, it is very nice ergonomically for branching randomly.

  if myGenerator.oneIn(3) {
    //This happens 1/3 of the time
  }else{
    //This happens 2/3 of the time
  }

- IMO distributions should be sources/generators themselves which just wrap another source. We could have a subprotocol of RandomNumberGenerator which just semantically guarantees uniform distribution, and then distributions that need it could be sure of the input distribution. Notice this doesn’t limit the distribution to only be used for Integers as they are in the demo. They can be used anywhere a source can be used.

I’d really like to maintain a clear line between generators and distributions (which I don’t think we need an additional protocol for). Distributions create values of a specific kind of type, with a particular distribution, which usually isn't suitable to use as the input for another algorithm that needs random data. Generators just pump out (hopefully) uniformly distributed bits, which distributions and other algorithms can then interpret and shape.

This gets to the heart of my main concern with the approach from the playground. Everything is special-cased. If the distribution only works for FixedWidthIntegers, then I can’t use it to create colors with that distribution, for example.

What I like about wrappers is that they fit anywhere a generator fits, so the receiver doesn’t really have to know/think about it at all. You don’t have to rewrite it for each type you want it to work for.

- Having a subprotocol for generators which can be rewound is extremely important for entire classes of real-world problems. I have spent a lot of time using this and it solves a LOT of problems. For example, I have a Lorem Ipsum Generator which takes Attributes and a CGSize to fill. It works by branching (using the Bool methods above) and then rewinding bits which don’t fit (If you just futz with the last part instead of generating appropriate clauses, it won’t look right). I also have a bunch of backtracking algorithms which rely on this rewind ability. Plus numerous visual effects which rely on a repeatable rewindable source.
  - Tl;dr: It isn’t enough to just have a seed, you need to be able to mark a state of a generator and return to that state later.

  My RepeatableRandomSource Protocol has 3 extra methods:
  - It takes a seed
  - It has a mark() method which returns a token
  - It has a returnToMark(_ mark:Mark) method which takes a token and restores the appropriate state

That’s neat, I’ve never used a generator like this! The RandomNumberGenerator protocol would certainly allow for a generator with these features, and libraries or future additions to the standard library could include that kind of protocol.

Thanks. I am coming to that conclusion. I can build most of what I need on a simple base. I guess I am sad that others won’t be able to share in it as easily, since it is a really nice/powerful way to code. I see so many visual bugs in UIs which have generated things randomly (e.g. things dance around or change when you resize them), and it is like this wouldn’t have happened if they had easy access to a repeatable/rewindable source.

One additional question. How do you ergonomically get a Double which doesn’t have a range, but also isn’t NaN?

Thanks,
Jon

···

On Jan 9, 2018, at 8:28 AM, Nate Cook <natecook@apple.com> wrote:

On Jan 9, 2018, at 4:12 AM, Jonathan Hull <jhull@gbis.com <mailto:jhull@gbis.com>> wrote:

On Jan 8, 2018, at 11:02 AM, Nate Cook via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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!

Nate

<Random.playground.zip>

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Give up*

···

Sent from my iPhone

On Jan 12, 2018, at 08:10, Alejandro Alonso via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>> wrote:

I am willing to give `.random()`. However I still wish to preserve `.random(in:)` and `.random()` on Bool.

Sent from my iPhone

On Jan 11, 2018, at 21:28, Xiaodi Wu <xiaodi.wu@gmail.com<mailto:xiaodi.wu@gmail.com>> wrote:

On Thu, Jan 11, 2018 at 9:17 PM, Alejandro Alonso via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>> wrote:
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))
Int.random()

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

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

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

// random boolean
Bool.random()

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.

You will have noted, surely, the evidence given above that in Apple's internal code the majority of uses of APIs equivalent to `Int.random()` are erroneous due to unintended modulo bias. Yes, what that would imply is that all the other languages did it wrong. Why do you feel it is so important to have a shorthand that enables no additional behavior in the face of overwhelming evidence that it is actively harmful?

Likewise, you acknowledge the inconsistency of Int.random() and Double.random(), yet again suggest a shorthand that enables no additional behavior in spite of the potential for misunderstanding. What advantage of having such an API do you see which could overcome this very glaring drawback?

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@swift.org<mailto:swift-evolution@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!

Nate

On Dec 2, 2017, at 9:50 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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@swift.org<mailto:swift-evolution@swift.org>> wrote:

On Dec 2, 2017, at 6:02 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@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.

-Kyle
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution