Skip to content

Commit 170b79f

Browse files
Azoyairspeedswift
authored andcommitted
Amend 0202 (#879)
1 parent d947810 commit 170b79f

File tree

1 file changed

+39
-36
lines changed

1 file changed

+39
-36
lines changed

proposals/0202-random-unification.md

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -90,39 +90,41 @@ To kick this off, the standard library will provide a default RNG. Each platform
9090

9191
For the core API, introduce a new protocol named `RandomNumberGenerator`. This type is used to define RNGs that can be used within the stdlib. Developers can conform to this type and use their own custom RNG throughout their whole application.
9292

93-
Then for the stdlib's default RNG implementation, introduce a new struct named `Random`. This struct contains a singleton called `default` which provides access to the methods of `RandomNumberGenerator`.
93+
Then for the stdlib's default RNG implementation, introduce a new struct named `SystemRandomNumberGenerator`.
9494

9595
Next, we will make extension methods for `FixedWidthInteger`, `BinaryFloatingPoint` and `Bool`. For numeric types, this allows developers to select a value within a range and swap out the RNG used to select a value within the range.
9696

9797
`FixedWidthInteger` example:
9898
```swift
9999
// Utilizes the standard library's default random
100-
/// (alias to Int.random(in: 0 ..< 10, using: &Random.default))
100+
// Alias to:
101+
// var rng = SystemRandomNumberGenerator()
102+
// Int.random(in: 0 ..< 10, using: &rng)
101103
let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
102104
let randomUIntFrom10Through100 = UInt.random(in: 10 ... 100, using: &myCustomRandomNumberGenerator)
103105

104106
// The following are examples on how to get full width integers
105107

106108
let randomInt = Int.random(in: .min ... .max)
107-
108-
// an alternative spelling could be:
109-
// let randomUInt = myCustomRandomNumberGenerator.next()
110-
// this takes advantage of the fact that generators can produce unsigned integers
111109
let randomUInt = UInt.random(in: .min ... .max, using: &myCustomRandomNumberGenerator)
112110
```
113111

114112
`BinaryFloatingPoint` example:
115113
```swift
116114
// Utilizes the standard library's default random
117-
// (alias to Float.random(in: 0 ..< 1, using: &Random.default))
115+
// Alias to:
116+
// var rng = SystemRandomNumberGenerator()
117+
// Float.random(in: 0 ..< 1, using: &rng)
118118
let randomFloat = Float.random(in: 0 ..< 1)
119119
let randomDouble = Double.random(in: 0 ... .pi, using: &myCustomRandomNumberGenerator)
120120
```
121121

122122
`Bool` example:
123123
```swift
124124
// Utilizes the standard library's default random
125-
// (alias to Bool.random(using: &Random.default))
125+
// Alias to:
126+
// var rng = SystemRandomNumberGenerator()
127+
// Bool.random(using: &rng)
126128
let randomBool1 = Bool.random()
127129
let randomBool2 = Bool.random(using: &myCustomRandomNumberGenerator)
128130
```
@@ -138,7 +140,9 @@ For `Collection` we add an extension method for collections to get a random elem
138140
let greetings = ["hey", "hi", "hello", "hola"]
139141

140142
// Utilizes the standard library's default random
141-
// (alias to greetings.randomElement(using: &Random.default))
143+
// Alias to:
144+
// var rng = SystemRandomNumberGenerator()
145+
// greetings.randomElement(using: &rng)!
142146
print(greetings.randomElement()!) // This returns an Optional
143147
print(greetings.randomElement(using: &myCustomRandomNumberGenerator)!) // This returns an Optional
144148
```
@@ -158,7 +162,9 @@ As a result of adding the random API, it only makes sense to utilize that power
158162
var greetings = ["hey", "hi", "hello", "hola"]
159163

160164
// Utilizes the standard library's default random
161-
// (alias to greetings.shuffle(using: &Random.default))
165+
// Alias to:
166+
// var rng = SystemRandomNumberGenerator()
167+
// greetings.shuffle(using: &rng)
162168
greetings.shuffle()
163169
print(greetings) // A possible output could be ["hola", "hello", "hey", "hi"]
164170

@@ -188,15 +194,9 @@ extension RandomNumberGenerator {
188194
}
189195

190196
// The stdlib RNG.
191-
public struct Random : RandomNumberGenerator {
192-
// Public facing API
193-
public static var `default`: Random {
194-
get { return Random() }
195-
set { /* Discard */ }
196-
}
197-
198-
// Prevents initialization of this struct
199-
private init() {}
197+
public struct SystemRandomNumberGenerator : RandomNumberGenerator {
198+
199+
public init() {}
200200

201201
// Conformance for `RandomNumberGenerator`, calls one of the crypto functions.
202202
public mutating func next() -> UInt64
@@ -215,7 +215,8 @@ extension Collection {
215215

216216
/// Uses the standard library's default RNG
217217
public func randomElement() -> Element? {
218-
return randomElement(using: &Random.default)
218+
var g = SystemRandomNumberGenerator()
219+
return randomElement(using: &g)
219220
}
220221
}
221222

@@ -225,9 +226,7 @@ extension Collection {
225226
// For those that are that unsure whether or not their range is empty or not,
226227
// they can if/guard check whether or not the range is empty beforehand, then
227228
// use these functions.
228-
extension FixedWidthInteger
229-
where Self.Stride : SignedInteger,
230-
Self.Magnitude : UnsignedInteger {
229+
extension FixedWidthInteger {
231230

232231
public static func random<T: RandomNumberGenerator>(
233232
in range: Range<Self>,
@@ -236,7 +235,8 @@ where Self.Stride : SignedInteger,
236235

237236
/// Uses the standard library's default RNG
238237
public static func random(in range: Range<Self>) -> Self {
239-
return Self.random(in: range, using: &Random.default)
238+
var g = SystemRandomNumberGenerator()
239+
return Self.random(in: range, using: &g)
240240
}
241241

242242
public static func random<T: RandomNumberGenerator>(
@@ -246,7 +246,8 @@ where Self.Stride : SignedInteger,
246246

247247
/// Uses the standard library's default RNG
248248
public static func random(in range: ClosedRange<Self>) -> Self {
249-
return Self.random(in: range, using: &Random.default)
249+
var g = SystemRandomNumberGenerator()
250+
return Self.random(in: range, using: &g)
250251
}
251252
}
252253

@@ -256,10 +257,7 @@ where Self.Stride : SignedInteger,
256257
// For those that are that unsure whether or not their range is empty or not,
257258
// they can simply if/guard check the bounds to make sure they can correctly form
258259
// ranges which a random number can be formed from.
259-
extension BinaryFloatingPoint
260-
where Self.RawSignificand : FixedWidthInteger,
261-
Self.RawSignificand.Stride : SignedInteger & FixedWidthInteger,
262-
Self.RawSignificand.Magnitude : UnsignedInteger {
260+
extension BinaryFloatingPoint where Self.RawSignificand : FixedWidthInteger {
263261

264262
public static func random<T: RandomNumberGenerator>(
265263
in range: Range<Self>,
@@ -268,7 +266,8 @@ where Self.RawSignificand : FixedWidthInteger,
268266

269267
/// Uses the standard library's default RNG
270268
public static func random(in range: Range<Self>) -> Self {
271-
return Self.random(in: range, using: &Random.default)
269+
var g = SystemRandomNumberGenerator()
270+
return Self.random(in: range, using: &g)
272271
}
273272

274273
public static func random<T: RandomNumberGenerator>(
@@ -278,7 +277,8 @@ where Self.RawSignificand : FixedWidthInteger,
278277

279278
/// Uses the standard library's default RNG
280279
public static func random(in range: ClosedRange<Self>) -> Self {
281-
return Self.random(in: range, using: &Random.default)
280+
var g = SystemRandomNumberGenerator()
281+
return Self.random(in: range, using: &g)
282282
}
283283
}
284284

@@ -294,7 +294,8 @@ extension Bool {
294294

295295
/// Uses the standard library's default RNG
296296
public static func random() -> Bool {
297-
return Bool.random(using: &Random.default)
297+
var g = SystemRandomNumberGenerator()
298+
return Bool.random(using: &g)
298299
}
299300
}
300301

@@ -309,7 +310,8 @@ extension Sequence {
309310

310311
/// Uses the standard library's default RNG
311312
public func shuffled() -> [Element] {
312-
return shuffled(using: &Random.default)
313+
var g = SystemRandomNumberGenerator()
314+
return shuffled(using: &g)
313315
}
314316
}
315317

@@ -320,7 +322,8 @@ extension MutableCollection {
320322

321323
/// Uses the standard library's default RNG
322324
public mutating func shuffle() {
323-
shuffle(using: &Random.default)
325+
var g = SystemRandomNumberGenerator()
326+
shuffle(using: &g)
324327
}
325328
}
326329
```
@@ -379,6 +382,6 @@ There were a bit of discussion for and against this. Initially I was on board wi
379382

380383
This was a very heavily discussed topic that we can't skip over.
381384

382-
I think we came into agreement that `range.random()` should be possible, however the discussion was around whether or not this is the primary spelling for getting random numbers. Having a range as the primary spelling makes it fairly simple to get a random number from. Ranges are also very desirable because it doesn't encourage modulo bias. Also, since we decided pretty early on that we're going to trap if for whatever reason we can't get a random number, this gave `range.random()` the excuse to return a non optional.
385+
I think we came into agreement that `range.randomElement()` should be possible, however the discussion was around whether or not this is the primary spelling for getting random numbers. Having a range as the primary spelling makes it fairly simple to get a random number from. Ranges are also very desirable because it doesn't encourage modulo bias. Also, since we decided pretty early on that we're going to trap if for whatever reason we can't get a random number, this gave `range.randomElement()` the excuse to return a non optional.
383386

384-
On the other end of the spectrum, we came into early agreement that `Collection.random()` needs to return an optional in the case of an empty collection. If ranges were the primary spelling, then we would need to create exceptions for them to return non optionals. This would satisfy the general use design, but as we agreed that `.random()` behaves more like `.first`, `.last`, `.min()`, and `.max()`. Because of this, `.random()` has to return an optional to keep the consistent semantics. This justifies the static functions on the numeric types as the primary spelling as they can be the ones to return non optionals. These static functions are also the spelling for how developers think about going about random numbers. "Ok, I need a random integer from x and y." This helps give these functions the upper hand in terms of discoverability.
387+
On the other end of the spectrum, we came into early agreement that `Collection.randomElement()` needs to return an optional in the case of an empty collection. If ranges were the primary spelling, then we would need to create exceptions for them to return non optionals. This would satisfy the general use design, but as we agreed that `.randomElement()` behaves more like `.first`, `.last`, `.min()`, and `.max()`. Because of this, `.randomElement()` has to return an optional to keep the consistent semantics. This justifies the static functions on the numeric types as the primary spelling as they can be the ones to return non optionals. These static functions are also the spelling for how developers think about going about random numbers. "Ok, I need a random integer from x and y." This helps give these functions the upper hand in terms of discoverability.

0 commit comments

Comments
 (0)