[Proposal] Random Unification

Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

- Alejando

7 Likes

I would very much like to see this implemented in pure Swift, i think it’s
only a hundred lines of code to implement anyway. That way, we also have
the same deterministic PRNGs across all Swift platforms. We should avoid
sticking more and more stuff into Foundation if it’s not necessary for
backwards compatibility. Instead it should go into its own Random core
module.

···

On Fri, Sep 8, 2017 at 11:52 AM, Alejandro Alonso via swift-evolution < swift-evolution@swift.org> wrote:

Hello swift evolution, I would like to propose a unified approach to
`random()` in Swift. I have a simple implementation here
https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This
implementation is a simple wrapper over existing random functions so
existing code bases will not be affected. Also, this approach introduces a
new random feature for Linux users that give them access to upper bounds,
as well as a lower bound for both Glibc and Darwin users. This change would
be implemented within Foundation.

I believe this simple change could have a very positive impact on new
developers learning Swift and experienced developers being able to write
single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation
changes if need be.

- Alejando

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

Here is some Swift 3 code that allows simple repeatable repeatable random sequences by conforming to a protocol:

I now use a slightly more complicated version of this which allows more complex types (like colors) to be added and constrained in more interesting ways than just from…to (e.g. Colors with a fixed lightness/brightness, but a range in hue). The base is pretty much the same.

I have found that user-facing code often needs the idea of repeatable/re-creatable randomness. I originally created it to create a sketchy version of lines that had random offsets added to points along the line. If the randomness wasn’t reproducible, then the sketchiness of the line would shift around randomly every time there was a change.

The code I have provided above is not useful for any sort of cryptography/security though. There are different reasons for randomness. Maybe that is something we should consider?

Thanks,
Jon

···

On Sep 8, 2017, at 9:52 AM, Alejandro Alonso via swift-evolution <swift-evolution@swift.org> wrote:

Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

- Alejando

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

It would be nice to leverage range support instead of a start and end value
IMHO.

···

On Fri, Sep 8, 2017 at 9:52 AM Alejandro Alonso via swift-evolution < swift-evolution@swift.org> wrote:

Hello swift evolution, I would like to propose a unified approach to
`random()` in Swift. I have a simple implementation here
https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This
implementation is a simple wrapper over existing random functions so
existing code bases will not be affected. Also, this approach introduces a
new random feature for Linux users that give them access to upper bounds,
as well as a lower bound for both Glibc and Darwin users. This change would
be implemented within Foundation.

I believe this simple change could have a very positive impact on new
developers learning Swift and experienced developers being able to write
single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation
changes if need be.

- Alejando

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

1 Like

Internally, I know that Swift has a Linux shim for arc4random. However,
it's unlikely that any modern random number generator facility in Swift
would rely on arc4random in such a way.

This topic has been broached on Swift Evolution previously. It's
interesting to me that Steve Canon is so certain that CSPRNGs are the way
to go. I wasn't aware that hardware CSPRNGs have come such a long way and
are so ubiquitous as to be feasible as a basis for Swift random numbers. If
so, great.

Otherwise, if there is any way that a software, non-cryptographically
secure PRNG is going to outperform a CSPRNG, then I think it's worthwhile
to have a (carefully documented) choice between the two. I would imagine
that for many uses, such as an animation in which you need a plausible
source of noise to render a flame, whether that is cryptographically secure
or not is absolutely irrelevant but performance may be key.

I have implemented Xorshift128+ and Xoroshiro128+ in a Swift numerics
library here:

They are, of course, *non*-cryptographically secure random number generator
algorithms, but these implementations rely on the hardware for a
cryptographically secure seed. A (useless) vignette for how my classes
would be used is:

let random = Random()!
// Initializing a `Random` from a cryptographically secure random seed may
fail,
// since it is possible that the system may not have enough entropy. Better
to be
// transparent about this than to proceed with some sort of less-than-secure
// alternative source of entropy.

let x = random.uniform() as Int

// You can also pass the desired result type as an argument.
let y = random.uniform(Int.self)

if x > y {
  print("Here's a random value between 0 and 42 (inclusive):")
  print(random.uniform(a: 0, b: 42))
} else {
  print("Here's a random value between -42 and 0 (inclusive):")
  print(random.uniform(a: -42, b: 0))
}

If you look through the design of the `PRNG` protocol, you'll see how easy
it is to implement functions that return a sequence of random values, in
some desired distribution. The overall design is inspired by the C++ STL
random number generator APIs, as well as some very cogent commentary on how
the C++ STL random number generator APIs are unnecessarily complicated. No
matter how far we simplify, though, I agree with commentators elsewhere
that it's important to allow the user to explicitly seed a PRNG; this
allows for the use of one such PRNG per thread, for example, instead of
seeding a new PRNG every time a random number is required.

···

On Fri, Sep 8, 2017 at 11:52 AM, Alejandro Alonso via swift-evolution < swift-evolution@swift.org> wrote:

Hello swift evolution, I would like to propose a unified approach to
`random()` in Swift. I have a simple implementation here
https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This
implementation is a simple wrapper over existing random functions so
existing code bases will not be affected. Also, this approach introduces a
new random feature for Linux users that give them access to upper bounds,
as well as a lower bound for both Glibc and Darwin users. This change would
be implemented within Foundation.

I believe this simple change could have a very positive impact on new
developers learning Swift and experienced developers being able to write
single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation
changes if need be.

- Alejando

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

Here is my wishlist:

• A protocol which allows random instances of any type which conforms (they would init from some number of random bytes passed to them… or better yet, they would be passed a source which can give them however many bytes of randomness they need)
• A choice between fast and secure randomness (and possibly other implementations)
• The optional ability to seed so that randomness can be reproduced when needed

It would also be nice to be able to constrain the created instances in some way. For scalars like Float and Int, this would mean being able to define a range which the random value falls in. For dimensional constructs (e.g. colors, points, sizes) you want to be able to constrain each axis individually. This is actually the hardest part to get right, especially if you are trying to keep things secure. Even for graphics applications where security isn’t the issue, biases in the randomness added by naive approaches like using % can be apparent to the eye.

I think what I would like to see is a protocol defining a source of randomness, which does that hard part for you (written in mail):

protocol RandomnessSource {
  init(seed: UInt64)
  func randomBytes(count: Int) -> [UInt64]

  //Default Imp of below provided from above
  func randomInt(in: ClosedRange<Int>? = nil) -> Int //nil means don’t constrain ( = nil is shorthand for convenience func randomInt() )
  func randomUInt(in: ClosedRange<UInt>? = nil) -> UInt
  func randomDouble(in: ClosedRange<Double>? = nil) -> Double
  func randomBool() -> Bool
}

…and then a protocol where a type can use that source to create random instances of themselves:

protocol Randomizable {
  static func random(_ source: RandomnessSource)
  static var fastRandom:Self {get} //Uses default “fast” source (default imp provided)
  static var secureRandom:Self {get} //Uses default “secure” source (default imp provided)
}

protocol RangeRandomizable: Randomizable {
  static func random(_ source: RandomnessSource, in: ClosedRange<Self>?)
  static func fastRandom(in: ClosedRange<Self>?) -> Self //Uses default “fast” source (default imp provided)
  static func secureRandom(in: ClosedRange<Self>?) -> Self //Uses default “secure” source (default imp provided)

  //Also provides default imp for random(_ source: RandomnessSource) {random(source, in: nil)}, etc...
}

Then things like points/sizes can use scalar types as building blocks:

extension CGFloat: RangeRandomizable {
  static func random(_ source: RandomnessSource, in:ClosedRange<CGFloat>){
    let doubleRange = //Convert range to double
    return CGFloat(source.randomDouble(in: doubleRange))
  }
}

extension CGSize: Randomizable {
  static func random(source: RandomnessSource){
    return CGSize(width: CGFloat.random(source), height: CGFloat.random(source))
  }

  //Can add convenience functions to bound width/height to range/constant
}

What I do behind the scenes in my own protocol is take a dictionary of named constraints (instead of a range) because I need a common interface for all dimensions:

public enum RandomSourceConstraint<T> {
    case none
    case constant(T)
    case min(T)
    case max(T)
    case range (T,T)
    case custom ( (RandomSourceValue)->T )

    //More code here...
}

Thanks,
Jon

···

On Sep 8, 2017, at 3:40 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Here is some Swift 3 code that allows simple repeatable repeatable random sequences by conforming to a protocol:
RandomSource is a Swift 3 collection which allows creation of a random sequence of any type which conforms to its RandomSourceCreatable protocol. · GitHub

I now use a slightly more complicated version of this which allows more complex types (like colors) to be added and constrained in more interesting ways than just from…to (e.g. Colors with a fixed lightness/brightness, but a range in hue). The base is pretty much the same.

I have found that user-facing code often needs the idea of repeatable/re-creatable randomness. I originally created it to create a sketchy version of lines that had random offsets added to points along the line. If the randomness wasn’t reproducible, then the sketchiness of the line would shift around randomly every time there was a change.

The code I have provided above is not useful for any sort of cryptography/security though. There are different reasons for randomness. Maybe that is something we should consider?

Thanks,
Jon

On Sep 8, 2017, at 9:52 AM, Alejandro Alonso via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

- Alejando

_______________________________________________
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

My 2c:

- I’d love to see some random number initializers get added as initializers on the numeric types. This is a long standing hole and it would be hugely valuable to fill it.
- I’d love to see several of the most common random kinds supported, and I agree it would be nice (but not required IMO) for the default to be cryptographically secure.
- We should avoid the temptation to nuke this mosquito with a heavy handed solution designed to solve all of the world’s problems: For example, the C++ random number stuff is crazily over-general. The stdlib should aim to solve (e.g.) the top 3 most common cases, and let a more specialized external library solve the fully general problem (e.g. seed management, every distribution imaginable, etc).

In terms of approach, I’d suggest looking at other libraries that are conceptually similar, e.g. the “simple random data” APIs for numpy:
https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.random.html

Things like “return a random number from 0 to 1.0, 0 to N, 0 to INTMAX, sample from a normal/gaussian distribution, and maybe one more should be enough.

-Chris

···

On Sep 8, 2017, at 9:52 AM, Alejandro Alonso via swift-evolution <swift-evolution@swift.org> wrote:

Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

Hello,

I have an implementation for reference
https://github.com/SusanDoggie/Doggie/blob/master/Sources/Doggie/Foundation/Random.swift

It's easy to remind that what's the range of random floating point number

Double.random(includeOne: true)
Double.random(includeOne: false)

···

2017-09-09 0:52 GMT+08:00 Alejandro Alonso via swift-evolution <swift-evolution@swift.org>:

Hello swift evolution, I would like to propose a unified approach to
`random()` in Swift. I have a simple implementation here
https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This
implementation is a simple wrapper over existing random functions so
existing code bases will not be affected. Also, this approach introduces a
new random feature for Linux users that give them access to upper bounds, as
well as a lower bound for both Glibc and Darwin users. This change would be
implemented within Foundation.

I believe this simple change could have a very positive impact on new
developers learning Swift and experienced developers being able to write
single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation
changes if need be.

- Alejando

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

Hello evolution,

I am very thankful for all the feedback, and I’ve been working on a design that tries to utilize everybody’s ideas. The link to what I have so far is here: https://gist.github.com/Azoy/15f0518df38df9b722d4cb17bafea4c1\. Please keep in mind this is just a design, no actual implementation as I would most likely need assistance in doing. The default source for randomness will use the OS’s unseeded CSPRNG. This current setup exposes developers to an API that lets them create their own RandomSources on the fly. I want to make the distinction that any existing or new sources of randomness that conform to UnsafeRandomSource are to be considered non cryptographically secure.

I would love to get this discussion flowing again, and I would love input on the design. A few things I came across with this design is that some may want to use ranges as an argument for the numeric types, RandomAccessCollection returning an optional when getting a random, and how to incorporate an API that allows distributions. I wanted to get input on how you feel about each of these topics as I’m indifferent about them all. I’m in no way saying this is the design we should go for, but I’m simply providing something I think we should build on or talk about.

- Alejandro

···

On Sep 8, 2017, 11:52 AM -0500, Alejandro Alonso via swift-evolution <swift-evolution@swift.org>, wrote:
Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

- Alejando

Hello once again Swift evolution community. I have taken the time to write up the proposal for this thread, and have provided an implementation for it as well. I hope to once again get good feedback on the overall proposal.

- Alejandro

···

On Sep 8, 2017, 11:52 AM -0500, Alejandro Alonso via swift-evolution <swift-evolution@swift.org>, wrote:
Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

- Alejando

Hello swift evolution once again, I’ve been hard at work considering every email and revising the proposal. I’ve made lots of changes and additions to the proposal to discuss some problems we’ve had (T.random), and walks through detailed design. You can see the proposal here: [Proposal] Random Unification by Azoy · Pull Request #760 · apple/swift-evolution · GitHub .

A big issue that lots of people pointed out was `T.random %` and to remove it completely from the API. To give a gist of why I continue to support T.random:

1. Modulo bias misuse is only a problem to types that conform to `BinaryInteger`. Why remove this functionality if only a portion of the types have the ability of misuse. `Double.random % 10` is a good example of where modulo isn’t implemented here as it produces the error, “'%' is unavailable: Use truncatingRemainder instead”.

2. `Int.random(in: Int.min … Int.max)` doesn’t work. For developers that actually rely on this functionality, the work around that was discussed earlier simply doesn’t work. `Int.min … Int.max`’s count property exceeds that of `Int`’s numerical range. A working work around would be something along the lines of `Int(truncatingIfNeeded: Random.default.next(UInt.self))` which creates a pain point for those developers. As the goal of this proposal to remove pain points regarding random, this change does the opposite.

I’m interested to hear if anymore discussion around this, or any other issues come up.

- Alejandro

···

On Sep 8, 2017, 11:52 AM -0500, Alejandro Alonso via swift-evolution <swift-evolution@swift.org>, wrote:
Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

- Alejando

IMO, we should have a `Random` or `RandomGenerator` interface and the stdlib can provide a `SystemRandom` that wraps arc4*/random(), and maybe a cryptographically secure one too (MT19937?).

protocol RandomGenerator {
    func uniform() -> Int
    func uniform() -> Double
    func uniformBytes(count: Int) -> Data
}

Then we could add things like, e.g. a protocol extension to generate normally distributed numbers (https://en.wikipedia.org/wiki/Box–Muller_transform\).

extension RandomGenerator {
    func normal<T: FloatingPoint>() -> (T, T) {
        // ...
    }
}

···

On Sep 8, 2017, at 10:08 AM, Shawn Erickson via swift-evolution <swift-evolution@swift.org> wrote:

It would be nice to leverage range support instead of a start and end value IMHO.
On Fri, Sep 8, 2017 at 9:52 AM Alejandro Alonso via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

- Alejando

_______________________________________________
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

Quick thoughts:

1. A stdlib-level random number facility should default to a host-provided CSPRNG as the generator. Anything less is really not justifiable. The throughput of modern HW-backed CSPRNGs exceeds that of all but the fastest PRNGs, anyway.

2. MT is not a CSPRNG.

– Steve

···

On Sep 8, 2017, at 1:31 PM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org> wrote:

IMO, we should have a `Random` or `RandomGenerator` interface and the stdlib can provide a `SystemRandom` that wraps arc4*/random(), and maybe a cryptographically secure one too (MT19937?).

protocol RandomGenerator {
    func uniform() -> Int
    func uniform() -> Double
    func uniformBytes(count: Int) -> Data
}

Then we could add things like, e.g. a protocol extension to generate normally distributed numbers (https://en.wikipedia.org/wiki/Box–Muller_transform\).

extension RandomGenerator {
    func normal<T: FloatingPoint>() -> (T, T) {
        // ...
    }
}

On Sep 8, 2017, at 10:08 AM, Shawn Erickson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

It would be nice to leverage range support instead of a start and end value IMHO.
On Fri, Sep 8, 2017 at 9:52 AM Alejandro Alonso via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

- Alejando

_______________________________________________
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

Range support is something that came up, and I think it’s a great idea as well. My question now is do we support both `CountableRange` and `CountableClosedRange`?

···

On Sep 8, 2017, 12:08 PM -0500, Shawn Erickson <shawnce@gmail.com>, wrote:
It would be nice to leverage range support instead of a start and end value IMHO.
On Fri, Sep 8, 2017 at 9:52 AM Alejandro Alonso via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>> wrote:
Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

- Alejando

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

Let me be precise: it is absolutely possible to outperform CSPRNGs. They have simply become fast enough that the performance gap doesn’t matter for most uses (let’s say amortized ten cycles per byte or less—whatever you are going to do with the random bitstream will be much more expensive than getting the bits was).

That said, yes, there should definitely be other options. It should be possible for users to get reproducible results from a stdlib random interface run-to-run, and also across platforms. That alone requires that at least one other option for a generator be present. There may also be a place for a very high-throughput generator like xorshiro128+.

All I’m really saying is that the *default* generator should be an os-provided unseeded CSPRNG, and we should be very careful about documenting any generator options.

– Steve

···

On Sep 8, 2017, at 8:09 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

This topic has been broached on Swift Evolution previously. It's interesting to me that Steve Canon is so certain that CSPRNGs are the way to go. I wasn't aware that hardware CSPRNGs have come such a long way and are so ubiquitous as to be feasible as a basis for Swift random numbers. If so, great.

Otherwise, if there is any way that a software, non-cryptographically secure PRNG is going to outperform a CSPRNG, then I think it's worthwhile to have a (carefully documented) choice between the two. I would imagine that for many uses, such as an animation in which you need a plausible source of noise to render a flame, whether that is cryptographically secure or not is absolutely irrelevant but performance may be key.

When Apple added GameplayKit to iOS, they added randomisation functions.
Could that implementation be repeated in Swift Foundation?

···

On Mon, Sep 11, 2017 at 2:48 AM, Susan Cheng via swift-evolution < swift-evolution@swift.org> wrote:

Hello,

I have an implementation for reference
https://github.com/SusanDoggie/Doggie/blob/master/Sources/Doggie/
Foundation/Random.swift

It's easy to remind that what's the range of random floating point number

Double.random(includeOne: true)
Double.random(includeOne: false)

2017-09-09 0:52 GMT+08:00 Alejandro Alonso via swift-evolution
<swift-evolution@swift.org>:
> Hello swift evolution, I would like to propose a unified approach to
> `random()` in Swift. I have a simple implementation here
> https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This
> implementation is a simple wrapper over existing random functions so
> existing code bases will not be affected. Also, this approach introduces
a
> new random feature for Linux users that give them access to upper
bounds, as
> well as a lower bound for both Glibc and Darwin users. This change would
be
> implemented within Foundation.
>
> I believe this simple change could have a very positive impact on new
> developers learning Swift and experienced developers being able to write
> single random declarations.
>
> I’d like to hear about your ideas on this proposal, or any implementation
> changes if need be.
>
> - Alejando
>
>
> _______________________________________________
> 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’d love to see several of the most common random kinds supported, and I agree it would be nice (but not required IMO) for the default to be cryptographically secure.

I would be very careful about choosing a "simple" solution. There is a log, sad history of languages trying to provide a "simple" random number generator and accidentally providing a powerful footgun instead. But:

- We should avoid the temptation to nuke this mosquito with a heavy handed solution designed to solve all of the world’s problems: For example, the C++ random number stuff is crazily over-general. The stdlib should aim to solve (e.g.) the top 3 most common cases, and let a more specialized external library solve the fully general problem (e.g. seed management, every distribution imaginable, etc).

That's not to say we need to have seven engines and twenty distributions like C++ does. The standard library is not a statistics package; it exists to provide basic abstractions and fundamental functionality. I don't think it should worry itself with distributions at all. I think it needs to provide:

  1. The abstraction used to plug in different random number generators (i.e. an RNG protocol of some kind).

  2. APIs on existing standard library types which perform basic randomness-related functions correctly—essentially, encapsulating Knuth. (Specifically, I think selecting a random element from a collection (which also covers generating a random integer in a range), shuffling a mutable collection, and generating a random float will do the trick.)

  3. A default RNG with a conservative design that will sometimes be too slow, but will never be insufficiently random.

If you want to pick elements with a Poisson distribution, go get a statistics framework; if you want repeatable random numbers for testing, use a seedable PRNG from XCTest or some other test tools package. These can leverage the standard library's RNG protocol to work with existing random number generators or random number consumers.

···

On Sep 9, 2017, at 10:31 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

I completely agree with you on all points, but I was wondering about the numeric types getting random initializers. I think it’s a great idea, but is this something that should be discussed in a separate proposal? My current goals for the proposal is creating the actual random API, then discussing ideas for additions to RandomAccessCollection+MutableCollection to include .random and .shuffle like Ben mentioned, along with the addition of the numeric initializers.

- Alejandro

···

On Sep 10, 2017, 12:31 AM -0500, Chris Lattner <clattner@nondot.org>, wrote:

On Sep 8, 2017, at 9:52 AM, Alejandro Alonso via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>> wrote:

Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

My 2c:

- I’d love to see some random number initializers get added as initializers on the numeric types. This is a long standing hole and it would be hugely valuable to fill it.
- I’d love to see several of the most common random kinds supported, and I agree it would be nice (but not required IMO) for the default to be cryptographically secure.
- We should avoid the temptation to nuke this mosquito with a heavy handed solution designed to solve all of the world’s problems: For example, the C++ random number stuff is crazily over-general. The stdlib should aim to solve (e.g.) the top 3 most common cases, and let a more specialized external library solve the fully general problem (e.g. seed management, every distribution imaginable, etc).

In terms of approach, I’d suggest looking at other libraries that are conceptually similar, e.g. the “simple random data” APIs for numpy:
https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.random.html

Things like “return a random number from 0 to 1.0, 0 to N, 0 to INTMAX, sample from a normal/gaussian distribution, and maybe one more should be enough.

-Chris

I'm okay with "unqualified" random being crypto-secure and qualifying non-crypto-secure generators as inferior, but I think that Unsafe is not the qualifier we are looking for. Everywhere else, Unsafe implies that misuse will cause unpredictable results/crashes, which is a different class of problems than the one that this tries to avoid. Maybe InsecureRandom? Pseudorandom? PredictableRandom?

The subtype relationships seem problematic to me: any UnsafeRandomSource is probably also a RandomSource. This means that you can't design an API requiring a RandomSource and assume that you'll get a crypto-secure one. Granted, anyone can pretend that their random is crypto-secure while it isn't, but I feel like this should be an important API hint.

I'm not convinced that `RandomSource` needs a `shared` property. By definition, you shouldn't be able to tell which RandomSource instance generated some random number, so it doesn't matter if you have multiple instances of it, or just one. It could be a flyweight and we'd be none the wiser. Requiring a shared instance also forces the implementation to be thread-safe, which is almost certainly not a desirable feature for an API meant to have a huge throughput.

To me, the tradeoff between "secure" and "insecure" seems to be that "secure" RNGs have an unpredictable result (which is suitable for crypto operations), and "insecure" RNGs can be seeded and repeated, which is a desirable feature in many scenarios. Then, there are some low-stake cases where that probably doesn't really matter (like in a dice-rolling app). That seems to be easy enough to represent with a base AnyRandomSource protocol, with sub-protocols RandomSource and PseudorandomSource (or whatever else seems to be a better name).

I think that there needs to be discussion about the random range for the static `random` properties. Right now, it can't return negative values, even for signed types. I feel that a random property on a type should use the entire domain of that type, and the range random properties can be used when you want a specific domain of values. If people aren't sure what Int.random or Double.random gets them, they won't use it, so I'd go for the simplest and most uniform possible definition for them: any finite value that the type can represent. (Also, your FixedWithInteger currently can't return Self.max, which I think is a bug.)

I have more comments, but it's getting late, so maybe I'll continue tomorrow.

FĂ©lix

···

Le 25 sept. 2017 Ă  21:57, Alejandro Alonso via swift-evolution <swift-evolution@swift.org> a Ă©crit :

Hello evolution,

I am very thankful for all the feedback, and I’ve been working on a design that tries to utilize everybody’s ideas. The link to what I have so far is here: https://gist.github.com/Azoy/15f0518df38df9b722d4cb17bafea4c1\. Please keep in mind this is just a design, no actual implementation as I would most likely need assistance in doing. The default source for randomness will use the OS’s unseeded CSPRNG. This current setup exposes developers to an API that lets them create their own RandomSources on the fly. I want to make the distinction that any existing or new sources of randomness that conform to UnsafeRandomSource are to be considered non cryptographically secure.

I would love to get this discussion flowing again, and I would love input on the design. A few things I came across with this design is that some may want to use ranges as an argument for the numeric types, RandomAccessCollection returning an optional when getting a random, and how to incorporate an API that allows distributions. I wanted to get input on how you feel about each of these topics as I’m indifferent about them all. I’m in no way saying this is the design we should go for, but I’m simply providing something I think we should build on or talk about.

- Alejandro

On Sep 8, 2017, 11:52 AM -0500, Alejandro Alonso via swift-evolution <swift-evolution@swift.org>, wrote:

Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

- Alejando

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

Instead of “UnsafeRandomSource”, I would call it “ReproducibleRandomSource”. I have also found that you often need to be able to “rewind” a reproducible random source for graphics applications:

  protocol ReproducibleRandomSource : RandomSource {
    init(seed: UInt64)
    func mark()->Int
    func returnToMark(_ mark:Int) //These could use something like an index instead of Int. My version just returns 1 the first time you call mark(), 2 the second time, etc...
  }

Also, I find that there are just a few primitive types I actually need from a random source:

• Double (full coverage of possible values)
• Double (in range of 0…1)
• UInt32

The following are also nice to have (built from the above), and commonly used:

• Bool / CoinFlip
• Int (positive value)
• FixedWidthInteger (of various sizes. Full coverage of possible values)
• Character/String
• CGFloat

Any other type can be built from these building blocks. I have a RandomSourceCreatable protocol which allows exactly that. Once you have that, you can put a method on random source which allows you to create any conforming type:

  extension RandomSource {
    func next<T:RandomSourceCreatable>(_ type: T.Type)->T
  }

This is extremely useful to create colors, offsets, etc…

One thing we need to definitely consider are constraints which people may want on the random value. For example, should an Int be positive? Should it be in a certain range? I have a “constraints” parameter on my RandomSourceCreatable protocol to handle this, and it works well, but I am not 100% happy with the ergonomics. I also have a variant with an “in range:” parameter that works for simple linear types like Ints and Floats. We could do something like:

  extension RandomSource {
    func next<T:RandomRangeCreatable>(_ type: T.Type, in range: ClosedRange<T>) -> T
  }

Finally, don’t underestimate the usefulness of coinFlip and func oneIn(_ number:UInt)->Bool. They let you quickly branch based on a random value:

  if source.oneIn(100) { //This will be true roughly 1 in 100 times
    //Do something occasionally
  }

Thanks,
Jon

···

On Sep 25, 2017, at 9:57 PM, Alejandro Alonso via swift-evolution <swift-evolution@swift.org> wrote:

Hello evolution,

I am very thankful for all the feedback, and I’ve been working on a design that tries to utilize everybody’s ideas. The link to what I have so far is here: https://gist.github.com/Azoy/15f0518df38df9b722d4cb17bafea4c1\. Please keep in mind this is just a design, no actual implementation as I would most likely need assistance in doing. The default source for randomness will use the OS’s unseeded CSPRNG. This current setup exposes developers to an API that lets them create their own RandomSources on the fly. I want to make the distinction that any existing or new sources of randomness that conform to UnsafeRandomSource are to be considered non cryptographically secure.

I would love to get this discussion flowing again, and I would love input on the design. A few things I came across with this design is that some may want to use ranges as an argument for the numeric types, RandomAccessCollection returning an optional when getting a random, and how to incorporate an API that allows distributions. I wanted to get input on how you feel about each of these topics as I’m indifferent about them all. I’m in no way saying this is the design we should go for, but I’m simply providing something I think we should build on or talk about.

- Alejandro

On Sep 8, 2017, 11:52 AM -0500, Alejandro Alonso via swift-evolution <swift-evolution@swift.org>, wrote:

Hello swift evolution, I would like to propose a unified approach to `random()` in Swift. I have a simple implementation here https://gist.github.com/Azoy/5d294148c8b97d20b96ee64f434bb4f5\. This implementation is a simple wrapper over existing random functions so existing code bases will not be affected. Also, this approach introduces a new random feature for Linux users that give them access to upper bounds, as well as a lower bound for both Glibc and Darwin users. This change would be implemented within Foundation.

I believe this simple change could have a very positive impact on new developers learning Swift and experienced developers being able to write single random declarations.

I’d like to hear about your ideas on this proposal, or any implementation changes if need be.

- Alejando

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