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