> On Nov 30, 2017, at 4:11 PM, Xiaodi Wu <xiaodi...@gmail.com> wrote: > > On Thu, Nov 30, 2017 at 5:29 PM, Jonathan Hull <jh...@gbis.com > <mailto:jh...@gbis.com>> wrote: > >> On Nov 30, 2017, at 2:30 PM, Xiaodi Wu <xiaodi...@gmail.com >> <mailto:xiaodi...@gmail.com>> wrote: >> >> On Thu, Nov 30, 2017 at 3:58 PM, Dave DeLong via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >> >> >>> On Nov 30, 2017, at 2:48 PM, Jonathan Hull via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>> >>> I would personally go with: >>> >>> Int.random //Returns a random Int >> >> “Type.random” is so rarely used as to not be worth the addition, IMO. If you >> really need a random element from the *entire* domain, then I think you >> should have to manually create the ClosedRange<T> yourself. >> >>> Int.random(in: ClosedRange<Int>) //Works for Comparable types. Gives a >>> result from the closed range. Closed Range is never empty. >> >> This is redundant. In order to pick a random element, you’re saying I should >> have to do “Int.random(0 ..< 10)”? The redundancy here is that I have to >> specify Int twice: once for the “.random” call, and again for the type of >> the range. We can do better than that. >> >>> [0,2,3].randomElement //Returns a random element from the collection >> >> I strongly believe this should be a method, not a property. Properties, like >> .first and .last, are expected to return the same value each time you access >> them. “.random” inherently breaks that. >> >> FWIW--and this isn't a vote, I know--I largely agree with Dave DeLong's >> conclusions above, and for substantially the same reasons. >>> >>> Then a version of each with a ‘using:’ parameter which takes a >>> generator/source: >>> >>> Int.random(using: RandomSource) //Returns a random Int using the given >>> source of randomness >>> Int.random(in: ClosedRange<Int>, using: RandomSource) >>> [0,2,3].randomElement(using: RandomSource) >>> >>> In my own RandomSource & RandomSourceCreatable protocols, I frequently use >>> random colors and sizes as well. The issue there is that you really want a >>> closed range for each dimension. I wish Swift had a better notion of >>> dimensionality baked into the language. >>> >>> What I ended up doing was having a “constraints” parameter which took an >>> array of constraints which corresponded to various dimensions. It works >>> for me, but it might be a bit complex for something in the standard library. >>> >>> Honestly, given the current capabilities of Swift what this really calls >>> for is custom initializers/functions for dimensional types: >>> >>> UIColor.random //This comes from the protocol >>> UIColor.random(hue: ClosedRange<CGFloat> = 0…1, saturation: >>> ClosedRange<CGFloat> = 0…1, brightness: ClosedRange<CGFloat> = 0…1, alpha: >>> ClosedRange<CGFloat> = 1…1) >>> //…and of course the same as above, but with ‘using:' >>> >>> Then you can easily get random colors which look like they belong together: >>> >>> let myColor = UIColor.random(saturation: 0.2…0.2, brightness: 0.6…0.6) >>> >>> There would probably also be a convenience version taking CGFloats and >>> passing them to the real function as ranges: >>> >>> let myColor = UIColor.random(saturation: 0.2, brightness: 0.6) >>> >>> >>> This means that our default RandomSource needs to be publicly available, so >>> that the custom functions can use it as the default… >> >> >> It does not. Having actually implemented some version of these APIs, it's >> readily apparent now to me that all custom types can simply call >> Int.random(in:) (or UnsafeRawBufferPointer<T>.random(byteCount:), or >> whatever else we want to have in the standard library) to get random values >> from the default RNG for any built-in type and size. The actual default >> random need never be exposed publicly, and since its functions are strictly >> redundant to these other APIs (which, of course, are the "currency" APIs >> that our purpose here is to design and make public), the default random is >> required only for internal implementation of the "currency" APIs and (a) is >> better off *not* exposed; (b) doesn't need to be of the same type as other >> RNGs, conform to the same protocols, or for that matter, does not even need >> to be a type or be written in Swift. > > I have also implemented some version of these APIs, both for Random and > RepeatablyRandom sources. > > I get what you are saying about just being able to use the constructs from > Int, etc…, but we still need a public default. Let me give a concrete > example of CGSize. Yes, we want to just use CGFloat’s random, but if we > don’t have a publicly available way to call the default then we have to > implement the same algorithm twice, which is problematic. (Code written in > Mail) > > static func random(width widthRange: ClosedRange<CGFloat>, height > heightRange: ClosedRange<CGFloat>, using source: RandomSource = .default) -> > CGSize { > let w = CGFloat.random(in: widthRange, using: source) > let h = CGFloat.random(in: heightRange, using: source) > return CGSize(width: w, height: h) > } > > Without the default I would have to have a second version which used > CGFloat(in:) in order to use the default source/generator, which means I > would have to update both places when I make changes. Much better to just > allow a default value for ‘using:'. > > Ah, this is assuming that one thinks it is a good idea to have `using:` > variants. Firstly, I'm not entirely sold on them. But let's suppose you've > convinced me. The example above illustrates a huge footgun: > > Why do people want to use a different RNG?
I *need* to be able to insert a repeatably random source, otherwise this whole exercise is useless/detrimental to me. My use case is that I need repeatably random values for various graphics effects, as well as to seed various content sources. If the source isn’t random, then things will change every time the user resizes something on the screen. For example, if I want a bezier curve to look hand-drawn, I break it into a number of smaller curves, and then offset the points by a small amount (by generating bounded random CGVectors). When the source is repeatable, this looks awesome. If the source is not repeatable, it will dance around whenever the user moves it. It feels flaky. It is much more important when it comes to generated content. There without a repeatable source, the content will change every time the user moves/resizes it (or even reloads the file from a save). You will find that there are a number of cases where games need this too... > A very common reason: to obtain a different distribution of random values. > Now, can you simply perform memberwise initialization as you show above and > obtain a final instance of the desired distribution simply because the stored > properties are drawn from that distribution? No no no no no! This > implementation is incorrect. I’m not following you here. > Not only is it not _problematic_ to require two distinct implementations, > it's almost certainly required if you want to have any hope of implementing > them correctly. I’m still not following. I don’t see why you would want different initialization patterns for different sources. The example I gave was about multi-dimensional structs/classes, so the distributions should be independent based on axis… Thanks, Jon
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution