SE-0202 Amendment Proposal: Rename Random to DefaultRandomNumberGenerator

Probably a silly idea, but if there were a common type named Default, you could define it something like:

extension Default {
  static var randomNumberGenerator: RandomNumberGenerator { return ... whatever you want to call it }
}

And this might be a nice home to all sorts of things that have platform defaults or whatever rather than putting static accessors in the main type itself?

1 Like

I have mixed feelings about this. I do agree that Random as a currency type is probably not the best. However, I do like the idea of a, (pardon the term), namespace that would wrap around various "Random" related items. However given that that is a whole can of worms, I would prefer to just keep it named Random. All the alternatives I've seen are too clunky for my liking.

4 Likes

I like the idea of renaming the Random type to DefaultRandomNumberGenerator. That name seems more descriptive of the actual purpose and behavior of the type.

I think it would be unfortunate, though, for the accessor to be so arduously long (e.g. DefaultRandomNumberGenerator.shared). At the call-site itself, Random.default seems descriptive enough while not being overly verbose.

I think this would be a good time to introduce a Random namespace:

enum Random {
    static var default: DefaultRandomNumberGenerator {
        return DefaultRandomNumberGenerator.shared
    }
}

An approach like this would allow us to keep current (and imo better) call-site semantics while updating the type name itself to be less misleading.

Eventually, other types could have accessors in the Random namespace as well. It would also be a good extension point for users to add their own sources of randomness.

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.