SE-0202 Amendment Proposal: Rename Random to DefaultRandomNumberGenerator

Do we want to do this for all the types that have a default? One occurrence of this naming should not really push the standard library to enforce this naming.

I think we need a more complete approach to default values. I worry that creating a precedence for the name default to be at the beginning is just avoid the problem.

I don't think it's really expected that you would ever see DefaultRandomNumberGenerator.shared at a call site. Given it's the default and all.

The one place users will need it is when defining their own random methods in parallel to the std lib ones, so they can have the same default. The length of the name doesn't seem particularly burdensome for this rare case.

5 Likes

This is an interesting notion – and is immediately interesting for our purposes here because it suggests an alternative possible namespace into which the default source of randomness could go.

Given it's unclear whether the std lib is the right place, long term, for other random-related things (as opposed to such things as distributions belonging in a separate package), it seems inappropriate to use this amendment as a vehicle for creating such a thing. This amendment is about the immediate need to rename this type to something more appropriate as part of the 4.2 release.

Then how about DefaultRandomSource.shared? I don't love it but it's shorter. Or RandomSource, which can be distinct from 3rd parties via Swift.RandomSource?:

We had lengthy discussion in the pitch phase about these kinds of names. These names are not good because the generators themselves are not a source of randomness. For example, calling into /dev/urandom. The file itself sources us randomness while Random takes that and generates a number.

4 Likes

I would like to echo something I said in the previous thread

2 Likes

Why are we over complicating this? 99% of the time you have some novice game develop who wants to roll a die or pick a card. They don't care if it's Mersenne Twister or a radioactive-isotope based RNG.

Progressive disclosure.

1 Like

I like Erica’s idea in particular because it opens the door to a Random.default that points to some standard generator that serves 90% of the need, much in the manner of other languages’ stdlibs, while setting up a sensible naming structure for more specialized RNGs.

If devs have to type DefaultRandomNumberGenerator.shared on a regular basis, Swift will never live it down.

6 Likes

DefaultRandomNumberGenerator.shared sound terrible unwieldy to me - Swift should not hold a competition who designs the ugliest and longest name. Were are the good old times of rnd()? Not every problem have to become rocket science 8(

To reiterate: if anyone has to type DefaultRandomNumberGenerator.shared on a regular basis, something has gone horribly wrong. Users are not expected to use this generator directly. They should use the higher-level APIs. They may use a PRNG of their own as an argument to those functions when paramterizing, but wouldn't ever need to pass in the default value.

If we did find people using it often, this would be a sign we've made it too inviting and that the name Random.default could be a cause of this.

6 Likes

It sounds to me that what you want then is a consensus that DefaultRandomNumberGenerator.default is the right name.

Points in favor:

  • It is highly descriptive and accurate
  • It does not mislead in using default rather than shared as new instances will be created.

Points against:

  • The tooling is overly long and difficult to type.
  • As @Paul_Cantrell notes, it may produce negative feelings about the language if used by third parties. (Reason: "If we did find people using it often, this would be a sign we've made it too inviting and that the name Random.default could be a cause of this.")

Let me ask:

  • Why does there need to be a default member at all rather than using DefaultRandomNumberGenerator()?
  • Why not use a static member that's set on first use, that can be shared?
  • If it is a (to quote from the first post of this thread) "default source", why not name it using those words or a close equivalent? Or is that just a misstatement that should be ignored?
  • Why not create a subtype for the default generator, allowing room for the language to grow or third parties to build on? (Edit: although this might make conformance difficult, and it apparently should not be inviting such customization as this appears to be a type meant to service the rest of the proposal, even though it's visible)

My apparently incorrect impression was that this name would occasionally should up in practical usage. If that's not the case, then I have no objection to a cumbersome name.

Because the higher-level APIs take a generator inout (because in the case of other sources, it needs to mutate the source). You can't create a mutable instance and pass it as an inout argument in one shot (so couldn't use it as a default).

I think a global shared instance, even a stateless one, could lead to problems with memory exclusivity.

3 Likes

I'm just going to repeat what I said in the previous thread, showing why brevity in these names isn't important, as a few people have raised this concern here.

The default generator is supplied as a default argument to methods on types that produce random values. The only place this type should appear is in function signature definitions.

For example, this code in the standard library (exact implementation details elided):

  public func randomElement<T: RandomNumberGenerator>(
    using generator: inout T
  ) -> Element?
  
  public func randomElement() -> Element? {
    return randomElement(using: &Random.default)
  }
}

will become:

  public func randomElement<T: RandomNumberGenerator>(
    using generator: inout T
  ) -> Element?
  
  public func randomElement() -> Element? {
    return randomElement(using: &DefaultRandomNumberGenerator.shared)
  }
}

The end user never has to type the long name. A user wanting a random element from the collection write collection.randomElement(). That's it. The stdlib generator is rarely typed explicitly.

4 Likes

I’ve been missing from discussions recently and will have to circle back and catch up, but given that I participated in review of this proposal I will chime in here.

Like several others, I disagree (strongly) with the proposed amendment:

There is essentially nothing else besides the default RNG that can legitimately lay claim to the name “Random”—and even those who wish to use it as a namespace can declare extensions of Random with their custom type.

Nothing about the proposed amended name adds clarity to “Random”: as it is a well-established term of art, it is clearly a RNG, and since it’s unadorned, it really can’t be anything other than the default RNG. Moreover, the name “Random” communicates that we have attempted to design a type appropriate for general use, not just a placeholder type to be superseded by a custom superior implementation (as the word “default” may imply to some readers). I do not think a strong case can be made that “default” and “number generator” are needful words in this context, and therefore the principle of “omit needless words” comes into play here.

That the core team is having trouble renaming “Random.default” is, to me, a symptom of the redundancy of the proposed amended name, and the solution that I would propose is to address the underlying cause: do not adopt this proposed amendment.

(To Erica’s question: why not have Random() instead of Random.default? It is a common mistake to seed a new PRNG every time a random number is needed; for some PRNGs, this has some overhead, but crucially it is not sound in terms of obtaining good randomness. Although the default Random could conceivably be designed so that these are not issues, to make Random() become the preferred way of using a shared default PRNG would be actively educating users to misuse other PRNGs.)

18 Likes

Imho this is a restriction whose removal is worthy of discussion (not because of this specific example).

4 Likes

Another restriction that would be nice to remove is the inability to specify a default argument for a generic function parameter. Right now, one has to manually overload a generic function to give it a default argument. For instance, this does not compile:

public func die<Source: RandomNumberGenerator>(sides n: Int, using generator: inout Source = &Random.default) -> () -> Int {
    ...
}

Even though there is only one possible meaning for this declaration, and that meaning is perfectly reasonable.

This is largely off topic, though.

I looked over the original proposal again and agree. In that case, it does seem reasonable to prefer a descriptive and correct. name over a terse yet misleading name

+1 to DefaultRandomNumberGenerator.shared

-1 to this proposal. For the big reason that Xiaodi stated: nothing other than the default random number generator could possibly use the name Random. There is nothing misleading in the name, therefore this proposal solves a non-problem.

After re-reading Benjamin’s pitch twice, I can not find any argument that the name Random is misleading. It contains only argument that users should consider other sources of randomness than the default — which means they should consider rolling their own implementations of RandomNumberGenerator protocol, I’m guessing mostly some PRNGs. This is already served by all the random methods on numeric types and collections that take the inout using: argument.

Renaming the struct Random to the pitched horribly overlong name would preclude future extensions as described by @Alejandro here, as well as ruin a convenient semantically meaningful point to hang user’s custom extension off of (Random serving as namespace, completely in line with Alejandro’s vision).

I really don’t understand the motivation for this amendment at all. I can see it causing a lot of harm and can not find a single reason why. Can somebody spell it out for me, please? (@bzamayo, @Ben_Cohen)

5 Likes