> On 13. Jan 2018, at 05:23, Nate Cook via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> On Jan 12, 2018, at 6:24 PM, Jonathan Hull via swift-evolution 
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> 
>> I think we have different definitions of consistency.  I am fine with the 
>> ergonomics of (0…100).random() as a convenience, but it really worries me 
>> here that everything is special cased.  Special cased things are fine for 
>> individual projects, but not the standard library.  We should make sure that 
>> the design is flexible and extensible, and that comes in part from having a 
>> consistent interface.
>> 
>> Also, as I said before, we really shouldn’t be doing these crazy contortions 
>> to avoid ‘random() % 100’.  Instead we should look for that pattern and 
>> issue with a warning + fixit to change it to random(in:).  I think that will 
>> be much more effective in actually changing the behavior in the long run.
> 
> I’m not sure what contortions you’re describing—from what I’ve seen, the 
> proposal author is going to revise the proposal to have these ways of 
> generating individual values:
> 
> In extensions to FixedWidthInteger and BinaryFloatingPoint:
> static func random(in: Range/ClosedRange<Self>, using: RandomNumberGenerator) 
> -> Self
> 
> In an extension to Bool:
> static func random(using: RandomNumberGenerator) -> Self
> 
> If someone still needs a full-width random value as a building-block for 
> generating random instances of other types, they should use the `next()` 
> method directly on a RandomNumberGenerator. In the example code you sent, you 
> could switch to using a RandomNumberGenerator instead of your 
> RandomSourceValue, or base your RandomSourceValue generation on a 
> RandomNumberGenerator instead of whatever random generator you’re using now.

I’m not sure what the current most-recent proposal is, but I think that’s fine 
for a minimal API.

The only thing I would add would be a convenience function which accesses a 
random element from a RandomAccessCollection.

- Karl

> 
>> Finally, tying everything to Range is extremely limiting.  I understand if 
>> we don’t want to add other types to the standard library, but I should be 
>> able to build on what we add to do it myself without having to reinvent the 
>> wheel for each type.  It is important to have a consistent story for these 
>> things (including multi-dimensional types) so that they can interoperate.
>> 
>> We really should be looking at GamePlayKit more for design inspiration.  
>> There are several use-cases there that are being blatantly ignored in this 
>> discussion.  For example, what if I want to randomly generate a game world 
>> (e.g. The square from The Battle For Polytopia” formerly “SuperTribes”)?  Or 
>> what if I want an effect where it randomly fades in letters from a String.  
>> (…).random() will be completely inadequate for these things.
> 
> The goal at this point is to build into the standard library the basis for 
> all kinds of other use cases. Your library is one such example of something 
> that can be built on top of the protocol and methods that are being proposed, 
> as are a variety of other tasks, as I tried to show in the playground.
> 
> What’s being proposed now is deliberately short of solving every need—the 
> additions would handle the hard stuff (correct and safe generation of 
> integers and floating-points, along with shuffling collections) and lay the 
> groundwork for other libraries to take things farther (by establishing the 
> RandomNumberGenerator, a default generator, and a pattern for their use).
> 
> Speaking of GameplayKit, you can make GKRandomSource conform to 
> RandomNumberGenerator in an extension, making all the GK... sources 
> generators. If you’re already depending on those random sources, you’d still 
> have access to them with the proposed model.
> 
> Nate
> 
>> Thanks,
>> Jon
>> 
>> 
>> 
>>> On Jan 12, 2018, at 5:11 AM, Letanyan Arumugam <letanya...@gmail.com 
>>> <mailto:letanya...@gmail.com>> wrote:
>>> 
>>> Nate’s design follows a consistent idea of getting a random value from some 
>>> set of values. Adding the static method random() to a type essentially 
>>> creates an implicit set which you yourself said leads to inconsistency 
>>> (Double/Int). Secondly I don’t see why random(in:) should be added when it 
>>> is just a different spelling for what is already provided. If my second 
>>> statement is incorrect and there’s something I’m missing please correct me?
>>> 
>>> I think that consistency outweighs the random trapping inconsistency, 
>>> however I would actually be fine if random returned an optional. Though the 
>>> way random is used would likely lead to less opportunities for a trap than 
>>> the other methods you mention. 
>>> 
>>> 
>>> Letanyan
>>> 
>>>> On 12 Jan 2018, at 04:39, Alejandro Alonso <aalonso...@outlook.com 
>>>> <mailto:aalonso...@outlook.com>> wrote:
>>>> 
>>>> If anything, Nate’s design is inconsistent as properties like `.first` and 
>>>> `.last` return an optional, and methods like `.min()` and `.max()` return 
>>>> an optional as well. Having `.random()` on ranges be an exception and 
>>>> return non optionals are inconsistent with other collection facilities, 
>>>> and with other collections that aren’t ranges that return optionals on 
>>>> `.random()`.
>>>> 
>>>> - Alejandro
>>>> 
>>>> On Jan 11, 2018, 12:06 PM -0600, Letanyan Arumugam via swift-evolution 
>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>, wrote:
>>>>> This is really cool and seems very powerful. However I don’t think we 
>>>>> should sacrifice consistency for extendability. Especially when the 
>>>>> extendability would not be what most people need. 
>>>>> 
>>>>> What I am basically trying to say is that. I think the proposals current 
>>>>> design direction fits better in a Random library rather than the Standard 
>>>>> Library. And Nate’s design more directly addresses the motivating points 
>>>>> of the proposal.
>>>>> 
>>>>> Letanyan
>>>>> 
>>>>>> 
>>>>>> Sure. Small disclaimer that this was originally written back in the 
>>>>>> Swift 1~2 days, so it is overdue for a simplifying rewrite.
>>>>>> 
>>>>>> Also, I should point out that the term “Source” has a special meaning in 
>>>>>> my code.  It basically means that something will provide an ~infinite 
>>>>>> collection of values of a type T.   I have what I call a 
>>>>>> “ConstantSource” which just wraps a T and gives it back when asked.  But 
>>>>>> then I have a bunch of other “sources" which let you create repeating 
>>>>>> patterns and do deferred calculations and things like that.  Finally I 
>>>>>> have a “RandomSource” which is part of what started this discussion.  
>>>>>> You set up a RandomSource with a set of constraints, and then it gives 
>>>>>> you random values of T that adhere to those constraints (e.g. colors 
>>>>>> with a range of hues but the same saturation) whenever you ask for them.
>>>>>> 
>>>>>> This is really useful for doing things like graphic effects because, for 
>>>>>> example, I can ask for a source of colors and a source of line widths 
>>>>>> and then get out a large variety of interesting patterns from the same 
>>>>>> algorithm.  I can make simple stripes with ConstantSources, or I can 
>>>>>> make repeating patterns of lines with repeating sources, or I can have 
>>>>>> random colors which look good together by using a RandomSource.  I can 
>>>>>> take a BezierPath and make it look hand-drawn by breaking it into a 
>>>>>> bunch of lines and then offset the points a small amount using a 
>>>>>> RandomSource of CGVectors.
>>>>>> 
>>>>>> Not sure how useful this concept of randomness (and pattern) is to 
>>>>>> others, but I find it immensely useful!  Not sure of the best way to 
>>>>>> implement it.  The way I do it is a type erased protocol with private 
>>>>>> conforming structs and then public initializers on the type-erasing box. 
>>>>>>  The end result is that I can just say:
>>>>>> 
>>>>>> let myConst = Source(1) //ConstantSource with 1 as a value
>>>>>> let myPattern = Source([1, 2]) //OrderedSource which repeats 1, then 2 
>>>>>> over and over forever
>>>>>> let myMeta = Source([myConst, myPattern]) //Will alternate between 
>>>>>> sub-sources in order. Can be nested.
>>>>>> //…and so on.
>>>>>> 
>>>>>> It is quite extensible and can make very complex/interesting patterns 
>>>>>> very easily.  What I like about it is that (well controlled) random 
>>>>>> values and patterns or constant values can be interchanged very easily.
>>>>>> 
>>>>>> The RandomSource has a RandomSourceCreatable Protocol that lets it take 
>>>>>> random bits and turn them into objects/structs of T adhering to the 
>>>>>> given constraints.  This is way more complex under the hood than it 
>>>>>> needs to be, but it works well in practice, and I haven’t gotten around 
>>>>>> to cleaning it up yet:
>>>>>> 
>>>>>> public protocol RandomSourceCreatable {
>>>>>>     associatedtype ConstraintType = Self
>>>>>>     
>>>>>>     
>>>>>> ///This should be implimented by simple types without internal components
>>>>>>     
>>>>>> static func createRandom(rnd value:RandomSourceValue, 
>>>>>> constraint:RandomSourceConstraint<ConstraintType>)->Self
>>>>>>     
>>>>>> ///This should be implimented by complex types with multiple axis of 
>>>>>> constraints
>>>>>>     
>>>>>> static func createRandom(rnd value:RandomSourceValue, 
>>>>>> constraints:[String:RandomSourceConstraint<ConstraintType>])->Self
>>>>>>     
>>>>>>     
>>>>>> ///Returns the proper dimension for the type given the constraints
>>>>>>     
>>>>>> static func dimension(given 
>>>>>> contraints:[String:RandomSourceConstraint<ConstraintType>])->RandomSourceDimension
>>>>>>     
>>>>>>     
>>>>>> ///Validates the given contraints to make sure they can create valid 
>>>>>> objects. Only needs to be overridden for extremely complex types
>>>>>>     static func validateConstraints(_ 
>>>>>> constraints:[String:RandomSourceConstraint<ConstraintType>])->Bool
>>>>>>     
>>>>>>     
>>>>>> ///Convienience method which provides whitelist of keys for implicit 
>>>>>> validation of constraints
>>>>>>     static var allowedConstraintKeys:Set<String> {get}
>>>>>>    }
>>>>>> 
>>>>>> Most of these things also have default implementations so you only 
>>>>>> really have to deal with them for complex cases like colors or points.  
>>>>>> The constraints are given using a dictionary with string keys and a 
>>>>>> RandomSourceConstraint value, which is defined like this:
>>>>>> 
>>>>>> public enum RandomSourceConstraint<T> {
>>>>>>     case none
>>>>>>     case constant(T)
>>>>>>     case min(T)
>>>>>>     case max(T)
>>>>>>     case range (T,T)
>>>>>>     case custom ( (RandomSourceValue)->T )
>>>>>> //A bunch of boring convenience code here that transforms values so I 
>>>>>> don’t always have to switch on the enum in other code that deals with 
>>>>>> this. I just ask for the bounds or constrained T (Note: T here refers to 
>>>>>> the type for a single axis as opposed to the generated type. e.g. 
>>>>>> CGFloat for a point) 
>>>>>>     }
>>>>>> 
>>>>>> I have found that this handles pretty much all of the constraints I 
>>>>>> need, and the custom constraint is useful for anything exotic (e.g. 
>>>>>> sig-figs).  The RandomSource itself has convenience inits when T is 
>>>>>> Comparable that let you specify a range instead of having to create the 
>>>>>> constraints yourself.
>>>>>> 
>>>>>> I then have conformed many standard types to RandomSourceCreatable so 
>>>>>> that I can create Sources out of them.  Here is CGPoint for reference:
>>>>>> 
>>>>>> extension CGPoint:RandomSourceCreatable {
>>>>>>     
>>>>>>     
>>>>>> public static func dimension(given 
>>>>>> contraints:[String:RandomSourceConstraint<CGFloat>])->RandomSourceDimension
>>>>>>  {
>>>>>>         
>>>>>> return RandomSourceDimension.manyWord(2)
>>>>>>     }
>>>>>>     
>>>>>>     public typealias ConstraintType = CGFloat
>>>>>>     public static var allowedConstraintKeys:Set<String>{
>>>>>>         return ["x","y"]
>>>>>>     }
>>>>>>     
>>>>>>     
>>>>>> public static func createRandom(rnd value:RandomSourceValue, 
>>>>>> constraints:[String:RandomSourceConstraint<CGFloat>])->CGPoint {
>>>>>>         let xVal = value.value(at: 0)
>>>>>>         let yVal = value.value(at: 1)
>>>>>>         
>>>>>> //Note: Ints have a better distribution for normal use cases of points
>>>>>>         let x = CGFloat(Int.createRandom(rnd: xVal, constraint: 
>>>>>> constraints["x"]?.asType({Int($0 * 1000)}) ?? .none))/1000
>>>>>>         let y = CGFloat(Int.createRandom(rnd: yVal, constraint: 
>>>>>> constraints["y"]?.asType({Int($0 * 1000)}) ?? .none))/1000
>>>>>>         return CGPoint(x: x, y: y)
>>>>>>     }
>>>>>>     }
>>>>>> 
>>>>>> Notice that I have a RandomSourceValue type that provides the random 
>>>>>> bits of the requested dimension. When I get around to updating this, I 
>>>>>> might do something closer to the proposal, where I would just pass the 
>>>>>> generator and grab bits as needed.  The main reason I did it the way I 
>>>>>> did is that it lets me have random access to the source very easily.  
>>>>>> 
>>>>>> The ‘asType’ method converts a constraint to work with another type (in 
>>>>>> this case Ints).
>>>>>> 
>>>>>> Colors are a bit more complicated, mainly because I allow a bunch of 
>>>>>> different constraints, and I also have validation code to make sure the 
>>>>>> constraints fit together properly. I also ask for different amounts of 
>>>>>> randomness based on whether it is greyscale or contains alpha. Just to 
>>>>>> give you a sense, here are the allowed constraint keys for a CGColor:
>>>>>> public static var allowedConstraintKeys:Set<String>{
>>>>>>         return ["alpha","gray","red","green","blue", "hue", 
>>>>>> "saturation", "brightness"]
>>>>>>     }
>>>>>> 
>>>>>> and here is the creation method when the keys are for RGBA (I have 
>>>>>> similar sections for HSBA and greyscale):
>>>>>> 
>>>>>> let rVal = value.value(at: 0)
>>>>>>     let gVal = value.value(at: 1)
>>>>>>     let bVal = value.value(at: 2)
>>>>>>     let aVal = value.value(at: 3)
>>>>>>     let r = CGFloat.createRandom(rnd: rVal, constraint: 
>>>>>> constraints["red"] ?? .range(0,1))
>>>>>>     let g = CGFloat.createRandom(rnd: gVal, constraint: 
>>>>>> constraints["green"] ?? .range(0,1))
>>>>>>     let b = CGFloat.createRandom(rnd: bVal, constraint: 
>>>>>> constraints["blue"] ?? .range(0,1))
>>>>>>     let a = CGFloat.createRandom(rnd: aVal, constraint: 
>>>>>> constraints["alpha"] ?? .constant(1.0))
>>>>>>             
>>>>>>     return self.init(colorSpace: CGColorSpaceCreateDeviceRGB(), 
>>>>>> components: [r,g,b,a])!
>>>>>> 
>>>>>> 
>>>>>> The end result is that initializing a source of CGColors looks like this 
>>>>>> (either parameter can be omitted if desired):
>>>>>> 
>>>>>> let colorSource:Source<CGColor> = Source(seed: optionalSeed, 
>>>>>> constraints:["saturation": .constant(0.4), "brightness": .constant(0.6)])
>>>>>> 
>>>>>> Anyway, I hope this was useful/informative.  I know the code is a bit 
>>>>>> messy, but I still find it enormously useful in practice.  I plan to 
>>>>>> clean it up when I find time, simplifying the RandomSourceValue stuff 
>>>>>> and moving from String Keys to a Struct with static functions for the 
>>>>>> constraints.  The new constraints will probably end up looking like this:
>>>>>> 
>>>>>> let colorSource:Source<CGColor> = Source(seed: optionalSeed, 
>>>>>> constraints:[.saturation(0.4), .brightness(0.4...0.6)])
>>>>>> 
>>>>>> Thanks,
>>>>>> Jon
>>>>>> 
>>>>>> 
>>>>>> _______________________________________________
>>>>>> swift-evolution mailing list
>>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>> 
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to