<div dir="ltr">On Fri, Nov 17, 2017 at 7:11 PM, Brent Royal-Gordon <span dir="ltr">&lt;<a href="mailto:brent@architechies.com" target="_blank">brent@architechies.com</a>&gt;</span> wrote:<br><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div><blockquote type="cite"><span class="gmail-"><div>On Nov 17, 2017, at 3:09 PM, Xiaodi Wu via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:</div><br class="gmail-m_1491587401867435223Apple-interchange-newline"></span><span class="gmail-"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">But actually, Int.random followed by % is the much bigger issue and a very good cautionary tale for why T.random is not a good idea. Swift should help users do the correct thing, and getting a random value across the full domain and computing an integer modulus is never the correct thing to do because of modulo bias, yet it&#39;s a very common error to make. We are much better off eliminating this API and encouraging use of the correct API, thereby reducing the likelihood of users making this category of error.<br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"></div></div></span></blockquote><div><br></div><div>Amen.</div><span class="gmail-"><br><blockquote type="cite"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">If (and I agree with this) the range-based notation is less intuitive (0..&lt;10.random is certainly less discoverable than Int.random), then we ought to offer an API in the form of `Int.random(in:)` but not `Int.random`. This does not preclude a `Collection.random` API as Alejandro proposes, of course, and that has independent value as Gwendal says.</div></div></blockquote></span></div><div><br></div><div>If we&#39;re not happy with the range syntax, maybe we should put `random(in:)`-style methods on the RNG protocol as extension methods instead. Then there&#39;s a nice, uniform style:</div><div><br></div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let diceRoll = rng.random(in: 1...6)</div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let card = rng.random(in: deck)</div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let isHeads = rng.random(in: [true, false])</div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let probability = rng.random(in: 0.0...1.0)<span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>// Special FloatingPoint overload</div><div><br></div><div>The only issue is that this makes the default RNG&#39;s name really important. Something like:</div><div><br></div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>DefaultRandom.shared.random(<wbr>in: 1...6)</div><div><br></div><div>Will be a bit of a pain for users.</div></div></blockquote><div><br></div><div>I did in fact implement this style of RNG in NumericAnnex, but I&#39;m not satisfied with the design myself. Not only is it a bit of an ergonomic thorn, there&#39;s also another drawback that actually has weighty implications:</div><div><br></div><div>Users aren&#39;t conditioned to reuse RNG instances. Perhaps, it is because it can &quot;feel&quot; wrong that multiple random instances should come from the *same* RNG. Instead, it &quot;feels&quot; more right to initialize a new RNG for every random number. After all, if one RNG is random, two must be randomer! This error is seen with some frequency in other languages that adopt this design, and they sometimes resort to educating users through documentation that isn&#39;t consistently heeded.</div><div><br></div><div>Of course, you and I both know that this is not ideal for performance. Moreover, for a number of PRNG algorithms, the first few hundred or thousand iterations can be more predictable than later iterations. (Some algorithms discard the first n iterations, but whether that&#39;s adequate depends on the quality of the seed, IIUC.) Both of these issues don&#39;t apply specifically to a default RNG type that cannot be initialized and always uses entropy from the global pool, but that&#39;s not enough to vindicate the design, IMO. By emphasizing *which* RNG instance is being used for random number generation, the design encourages non-reuse of non-default RNGs, which is precisely where this common error matters for performance (and maybe security).</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div>Maybe we call the default RNG instance `random`, and then give the `random(in:)` methods another name, like `choose(in:)`?</div><div><br></div><div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let diceRoll = random.choose(in: 1...6)</div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let card = random.choose(in: deck)</div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let isHeads = random.choose(in: [true, false])</div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let probability = random.choose(in: 0.0...1.0)</div></div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span></div><div><div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let diceRoll = rng.choose(in: 1...6)</div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let card = rng.choose(in: deck)</div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let isHeads = rng.choose(in: [true, false])</div><div><span class="gmail-m_1491587401867435223Apple-tab-span" style="white-space:pre-wrap">        </span>let probability = rng.choose(in: 0.0...1.0)</div></div></div><div><br></div><div>This would allow us to keep the default RNG&#39;s type private and expose it only as an existential—which means more code will treat RNGs as black boxes, and people will extend the RNG protocol instead of the default RNG struct—while also putting our default random number generator under the name `random`, which is probably where people will look for such a thing.</div></div></blockquote><div><br></div><div>I&#39;ve said this already in my feedback, but it can get lost in the long chain of replies, so I&#39;ll repeat myself here because it&#39;s relevant to the discussion. I think one of the major difficulties of discussing the proposed design is that Alejandro has chosen to use a property called &quot;random&quot; to name multiple distinct functions which have distinct names in other languages. In fact, almost every method or function is being named &quot;random.&quot; We are tripping over ourselves and muddling our thinking (or at least, I find myself doing so) because different things have the exact same name, and if I&#39;m having this trouble after deep study of the design, I think it&#39;s a good sign that this is going to be greatly confusing to users generally.</div><div><br></div><div>First, there&#39;s Alejandro&#39;s _static random_, which he proposes to return an instance of type T given a type T. In Python, this is named `randint(a, b)` for integers, and `random` (between 0 and 1) or `uniform(a, b)` for floating-type types. The distinct names reflect the fact that `randint` and `uniform` are mathematically quite different (one samples a *discrete* uniform distribution and the other a *continuous* uniform distribution), and I&#39;m not aware of non-numeric types offering a similar API in Python. These distinct names accurately reflect critiques from others on this list that the proposed protocol `Randomizable` lumps together types that don&#39;t share any common semantics for their _static random_ method, and that the protocol is of questionable utility because types in general do not share sufficient semantics such that one can do interesting work in generic code with such a protocol.</div><div><br></div><div>Then there&#39;s Alejandro&#39;s _instance random_, which he proposes to return an element of type T given a instance of a collection of type T. In Python, this is named &quot;choice(seq)&quot; (for one element, or else throws an error) and &quot;sample(seq, k)&quot; (for up to k elements). As I noted, Alejandro was right to draw an analogy between _instance random_ and other instance properties of a Collection such as `first` and `last`. In fact, the behavior of Python&#39;s &quot;choice&quot; (if modified to return an Optional) and &quot;sample&quot;, as a pair, would fit in very well next to Swift&#39;s existing pairs of `first` and `prefix(k)` and `last` and `suffix(k)`. We could trivially Swiftify the names here; for example:</div><div><br></div><div>```</div><div>[1, 2, 3].first</div><div>[1, 2, 3].any // or `choice`, or `some`, or...</div><div>[1, 2, 3].last</div><div><br></div><div>[1, 2, 3].prefix(2)</div><div>[1, 2, 3].sample(2)</div><div>[1, 2, 3].suffix(2)</div><div>```</div><div><br></div><div>I&#39;m going to advocate again for _not_ naming all of these distinct things &quot;random&quot;. Even in conducting this discussion, it&#39;s so hard to keep track of what particular function a person is giving feedback about.</div><div><br></div><div><br></div></div></div></div>