> On Nov 16, 2017, at 8:12 PM, Alejandro Alonso via swift-evolution > <[email protected]> wrote: > > While this could work, I don’t believe this aligns with Swift. > SecRandomCopyBytes and arc4random_buf do it this way because of the languages > they were built in, and for SecRandomCopyBytes, it needs to also return an > error (in the form of its return value). For custom generators this doesn’t > make sense to me because it seems as if each generator will have the same > code to return an integer of a different size from what it’ll be producing. I > really like Xiaodi’s solution to explicitly state what type of integer a > custom generator will return, and as a default implementation, we provide a > way to transform that. > > - Alejandro
The random source does not know about integers or floats or colors. It just provides randomness. Higher level code is what determines for instance how to generate (for example) a random number between 1 and 27 with equal probability - which could theoretically require more than a 64 bits of randomness (with probability of that depending on luck and the algorithm used) A double is not a uniform distribution, so likewise simply casting a 64 bit integer value to a double would not yield appropriate results (you’d have a significant chance of NaN values, for instance) Thats why I would recommend having the random source just be a sequence of bytes. The higher level API choosing random elements from a Strideable or shuffling an array *should* be the interfaces that developers use, rather than directly reading from the random source I proposed “read” below because it is compatible with the signature on InputStream, which both would allow you to easily bridge /dev/random or /dev/urandom in as well as have predefined data as a source of randomness for predictable testing. Predictable randomness and multiple sources of randomness are important in a few scenarios, including gaming where the same “random” choices need to be made locally for each player to keep the games in sync. -DW > > On Nov 15, 2017, 11:26 AM -0600, Nate Cook <[email protected]>, wrote: >>> On Nov 13, 2017, at 7:38 PM, Xiaodi Wu <[email protected] >>> <mailto:[email protected]>> wrote: >>> >>> On Mon, Nov 13, 2017 at 7:12 PM, Alejandro Alonso <[email protected] >>> <mailto:[email protected]>> wrote: >>> After thinking about this for a while, I don’t agree with with an >>> associated type on RandomNumberGenerator. I think a generic >>> FixedWidthInteger & UnsignedInteger should be sufficient. If there were an >>> associated type, and the default for Random was UInt32, then there might be >>> some arguments about allowing Double to utilize the full 64 bit precision. >>> We could make Random32 and Random64, but I think people will ask why there >>> isn’t a Random8 or Random16 for those bit widths. The same could also be >>> said that any experienced developer would know that his PRNG would be >>> switched if he asked for 32 bit or 64 bit. >>> >>> I don't understand. Of course, Double would require 64 bits of randomness. >>> It would obtain this by calling `next()` as many times as necessary to >>> obtain the requisite number of bits. >>> >>> At base, any PRNG algorithm yields some fixed number of bits on each >>> iteration. You can certainly have a function that returns an arbitrary >>> number of random bits (in fact, I would recommend that such an algorithm be >>> a protocol extension method on RandomNumberGenerator), but it must be built >>> on top of a function that returns a fixed number of bits, where that number >>> is determined on a per-algorithm basis. Moreover--and this is >>> important--generating a random unsigned integer of arbitrary bit width in a >>> sound way is actually subtly _different_ from generating a floating-point >>> value of a certain bit width, and I'm not sure that one can be built on top >>> of the other. Compare, for example: >>> >>> https://github.com/xwu/NumericAnnex/blob/c962760bf974a84ec57d8c5e94c91f06584e2453/Sources/PRNG.swift#L157 >>> >>> <https://github.com/xwu/NumericAnnex/blob/c962760bf974a84ec57d8c5e94c91f06584e2453/Sources/PRNG.swift#L157> >>> https://github.com/xwu/NumericAnnex/blob/c962760bf974a84ec57d8c5e94c91f06584e2453/Sources/PRNG.swift#L316 >>> >>> <https://github.com/xwu/NumericAnnex/blob/c962760bf974a84ec57d8c5e94c91f06584e2453/Sources/PRNG.swift#L316> >>> >>> (These are essentially Swift versions of C++ algorithms.) >>> >>> Basically, what I'm saying is that RandomNumberGenerator needs a `next()` >>> method that returns a fixed number of bits, and extension methods that >>> build on that to return T : FixedWidthInteger & UnsignedInteger of >>> arbitrary bit width or U : BinaryFloatingPoint of an arbitrary number of >>> bits of precision. Each individual RNG does not need to reimplement the >>> latter methods, just a method to return a `next()` value of a fixed number >>> of bits. You are welcome to use my implementation. >> >> An alternative to this is to have the random generator write a specified >> number of bytes to a pointer’s memory, as David Waite and others have >> suggested. This is same way arc4random_buf and SecRandomCopyBytes are >> implemented. Each random number generator could then choose the most >> efficient way to provide the requested number of bytes. The protocol could >> look something like this: >> >> protocol RandomNumberGenerator { >> /// Writes the specified number of bytes to the given pointer’s memory. >> func read(into p: UnsafeMutableRawPointer, bytes: Int) >> } >> >> This is less user-friendly than having a next() method, but I think that’s a >> good thing—we very much want people who need a random value to use >> higher-level APIs and just pass the RNG as a parameter when necessary. >> >> Nate >> >> > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
