[swift-evolution] [Proposal] Random Unification
Xiaodi Wu
xiaodi.wu at gmail.com
Tue Jan 2 12:29:53 CST 2018
On Tue, Jan 2, 2018 at 10:19 AM, Dave DeLong via swift-evolution <
swift-evolution at swift.org> wrote:
> Just skimmed through the updated proposal and am weighing in with my naïve
> opinions:
>
>
> - I’m still highly skeptical of a static “T.random” API. I’ve yet to
> see a convincing example where it’d be useful to pick a value from the
> range of all possible values. The only truly useful one I’ve seen is “pick
> a random bool”, which could easily be done via “[true, false].random()"
>
> - I much prefer the GameplayKit API[0], which breaks the idea of
> randomness up in to 2 separate concepts:
> - A “Source” → Where the random numbers come from
> - A “Distribution” → Initialized with a source, it makes sure the
> produced numbers exhibit a specific distribution over multiple samplings.
> Ie, a uniform distribution vs a Gaussian distribution, or something like “I
> want to pick a card from a deck but bias choices towards Spades or Aces”.
> I’m also reminded of the anecdote of how iTunes had to modify their
> “playlist shuffle” algorithm to be less random[1], because the true
> randomness would do weird things that made it seem not random. Spotify had
> the same problem and solution[2].
> - Breaking things up like this would also make it easier to test
> randomness (by using a replay-able source) but that still follow the
> parameters of your app (that it has a bell-curve distribution of
> probabilities, for example)
>
> - I’d still really really really like to see how this could be done
> as two separate things:
> - A minimal implementation in the Standard Library (like, defining
> the base Source and Distribution protocols, with a single default
> implementation of each)
> - A proposal for a more complete “non-standard library” where the
> larger array of functionality would be contained. For example, IMO I don’t
> think the shuffling stuff needs to be in the standard library. This is also
> where all the cryptographically secure stuff (that your typical app
> developer does not need) would live.
>
> - The “random” element of a collection/range should be “func
> random() → Element?”, not “var random: Element?”. Property values shouldn't
> change between accesses. Ditto the static “Randomizable.random” property.
>
> - What do you think about actively discouraging people from using the
> modulo operator to create a range? It could be done by having the RNGs
> return a “RandomValue<T>” type, then defining a mod operator that takes a
> RandomValue<T> and a T (?), and then giving it a deprecation warning +
> fixit. Not sure if that’d be worth the type overhead, but I’m very much in
> favor of encouraging people towards better practices.
>
> - I’m +1 on crashing if we can’t produce a random number.
>
> - What do you think about the philosophical difference of
> Type.random(using:) vs Type.init(randomSource:)?
>
>
> Dave
>
> [0]: https://developer.apple.com/documentation/gameplaykit/gkrandom
> [1]: https://www.youtube.com/watch?v=lg188Ebas9E&feature=youtu.be&t=719
> [2]: https://labs.spotify.com/2014/02/28/how-to-shuffle-songs/
>
>
I think these are some excellent points. Earlier, I think, others also
emphasized this idea of exploring what a really minimal implementation in
the standard library would look like, and I've been thinking about this
overnight.
There is much that is commendable about Alejandro's proposal, but I agree
that there is more than needs to be in the standard library. Here's what I
think the shape of a minimal API would look like, which would
simultaneously enable others to implement their desired functionality as an
end user:
- We need very performant, but otherwise barebones, access to system
randomness so that it can be a building block for everything else. Because
this is so special in that it cannot be seeded or initialized, unlike other
RNGs, we don't need this to be a type, and it doesn't need to conform to a
`RandomNumberGenerator` protocol. It can be as straightforward as one or
both of:
-- A global `func random() -> UInt32`, which is essentially `arc4random` on
macOS/iOS and reads from an appropriate secure source on Linux and other
platforms. One pro of having such a method is that it's a drop-in
replacement for `arc4random()` that's _very_ convenient as a primitive to
build up other random operations; one con is that it encourages modulo
bias, although fortunately mostly only with UInt32.
-- An extension method on `UnsafeMutableRawBufferPointer` named `func
copyRandomBytes()`. This would look a lot like Apple's `SecCopyRandomBytes`
and BSD's `arc4random_buf`.
- Having established the primitive, then we can ask what is the minimum
_useful_ functionality for an end user. I think the answer is a very
judicious subset of the currently proposed functionality:
-- An extension method or property (`random()` or `random`) on
`RandomAccessCollection`.
-- An extension method or property (`random()` or `random`) on `Range where
Bound : SignedInteger`, `Range where Bound : UnsignedInteger`, `Range where
Bound : BinaryFloatingPoint`.
- One advantage of abandoning `Randomizable` and spellings like
`Int.random` and `Double.random` is limiting possible modulo bias in using
the former, and also eliminating the lack of semantic guarantees about the
implicit bounds (which for Int are Int.min...Int.max, but for Double must
be 0..<1). There can be no confusion about `(0.0..<1.0).random`.
- An advantage inherent to abandoning the `randomIn:` or `random(in:)`
spelling is that users must now explicitly address what to do about an
empty range, which in my view is better than an implicit trap that may or
may not happen deterministically during testing.
- A final advantage over the current proposal is that we collapse three
very similar spellings with subtle usage differences but entirely
overlapping functionality--`Int.random`, `Int.random(in:)`, and
`Range.random`--into one with well-defined semantics. A user learns once
and can have good understanding of others' code and write their own without
hidden pitfalls.
The end result of such a minimalist design is that we introduce no new
types, no new protocols, and only three or four new functions. They all
have very defined semantics, and together they enable both basic use and
provide the building blocks for advanced users to create their own PRNGs
that fit their advanced cryptographic or non-cryptographic needs.
Thoughts?
On Jan 2, 2018, at 1:35 AM, Alejandro Alonso via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> 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:
> https://github.com/apple/swift-evolution/pull/760 .
>
> 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 at 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20180102/6e91a3cb/attachment.html>
More information about the swift-evolution
mailing list