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> 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>, 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
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> 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