(0 ..< 10).random() would return an optional
Int.random(in: 0 ..< 10) would return a non optional (this is the one that 
crashes on bad range)

Sent from my iPhone

On Jan 12, 2018, at 07:11, 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

_______________________________________________
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

Reply via email to