I agree that as an operation, .randomElement() feels closer to .first and .last (check that there's an element in the collection) than to the subscript (trap).
However, it *is* a usability sore spot on collection literals and ranges made from literals. Nobody writes [1,2,3].first, because the first element is known. However, [1,2,3].randomElement() makes sense (as does (0...6).randomElement()), and we know that it has to return a value. This could be solved with non-type generic parameters and making literals use them, but I don't see that happening for Swift 5. I think that I'm favorable to having a trapping version of it on ranges that shadows an Optional one that comes from a Collection extension until there's a better option. > Le 5 oct. 2017 à 22:16, David Hart via swift-evolution > <swift-evolution@swift.org> a écrit : > > > On 6 Oct 2017, at 06:25, David Hart via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: > >> >> On 5 Oct 2017, at 20:23, Ben Cohen via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >> >>> >>>> On Oct 5, 2017, at 10:58, Nate Cook via swift-evolution >>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>> >>>> The edge case is really the same (empty ranges), it’s about what we do >>>> with the edge case. If we include the methods on integer types, usage will >>>> look like this: >>>> >>>> let x = Int.random(in: 0..<5) // 3 >>>> let y = Int.random(in: 0..<0) // runtime error >>>> >>> >>> These examples are a bit misleading, because they use literals. Sometimes >>> they will, sure, but in practice, many use cases would define >>> Int.random(in: 0..<array.count) or similar, which has just the same >>> pitfalls as array.random(). >>> >>> p.s. ideally Int.random(in: 0..<0) would be a compile time error... >>> >>>> If we only have the collection methods, usage will look like this: >>>> >>>> let x = (0..<5).random()! // 3 >>>> let y = (0..<0).random()! // runtime error >>>> >>> >>> I don’t know if it’s a given that we must make randomElement optional. I’m >>> on the fence as to whether it should be optional vs trap on empty. >> >> I vote for making them optional because doing otherwise would be >> inconsistent with first and last, no? > > I want to be able to check for empty at the same time as I get the value, > exactly like I do first first and last: > > if let rand = array.randomElement() { > // use rand > } else { > // handle empty > } > > OR > > guard let rand = array.randomElement() else { > // handle empty > } > > I also like those optional returning properties because it’s a small reminder > from the type system to check for the corner case (empty) and makes them > explicit when reading code. With a trapping function, I would often forget to > handle empty and it wouldn’t jump out at me when reading code. > >>> Another option is to shadow randomElement on closed range to be >>> non-optional. >>> >>>> But my suspicion is that lots of people will write things like this: >>>> >>>> guard let x = (0..<5).random() >>>> else { fatalError("not gonna happen") } >>>> >>>> I’d rather have the numeric methods trap than add the optional unwrapping >>>> step to every one of these calls. For me, getting a random number and >>>> picking a random element of a collection are two different >>>> operations—where it’s common to work with empty collections, especially in >>>> generic code, trying to get a random value from an empty range is really a >>>> programming error. I think it’s okay for them to have slightly different >>>> semantics. >>>> >>>> Nate >>>> >>>> >>>>> On Oct 5, 2017, at 12:27 PM, Alejandro Alonso <aalonso...@outlook.com >>>>> <mailto:aalonso...@outlook.com>> wrote: >>>>> >>>>> Rather 0 ..< 0 my bad. I think if we include closedcountable, then there >>>>> needs to be support for countable, but there are edge cases where users >>>>> can input invalid ranges for countable. >>>>> >>>>> Enviado desde mi iPhone >>>>> >>>>> El oct. 5, 2017, a la(s) 12:22, Alejandro Alonso via swift-evolution >>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> escribió: >>>>> >>>>>> I agree with Ben here because users can still enter an invalid range >>>>>> with the static function. I.E. Int.random(in: 0 ... 0). >>>>>> I would really prefer excluding these static functions from numeric >>>>>> types. >>>>>> >>>>>> - Alejandro >>>>>> >>>>>> El oct. 5, 2017, a la(s) 12:03, Nate Cook via swift-evolution >>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> escribió: >>>>>> >>>>>>>> On Oct 5, 2017, at 11:30 AM, Ben Cohen via swift-evolution >>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>>>>> >>>>>>>>> On Oct 4, 2017, at 9:12 PM, Chris Lattner via swift-evolution >>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>>>>>> >>>>>>>>>> ``` >>>>>>>>>> extension Int { >>>>>>>>>> static func random(in range: Countable{Closed}Range<Int>) -> Int >>>>>>>>>> } >>>>>>>>> >>>>>>>>> Nice. Should these be initializers like: >>>>>>>>> >>>>>>>>> extension Int { >>>>>>>>> init(randomIn: Countable{Closed}Range<Int>) >>>>>>>>> } >>>>>>>>> >>>>>>>> >>>>>>>> I don’t see much of a case for making it it random(in: >>>>>>>> SpecificCollection) instead of genericCollection.random(). >>>>>>> >>>>>>> I see a couple points in favor of these static methods (or >>>>>>> initializers) on the numeric types: >>>>>>> >>>>>>> 1) The collection method will need to return an optional to match the >>>>>>> semantics of existing methods (like min()). If this is the only method >>>>>>> available, every time someone needs a random value in the range 1...10, >>>>>>> they’ll need to unwrap the result (with either force unwrapping, which >>>>>>> people will complain about, or some kind of conditional binding, which >>>>>>> is its own problem). Even if the semantics are the same (trapping on an >>>>>>> empty range), the user experience of using a non-optional method will >>>>>>> be better. >>>>>>> >>>>>>> 2) Floating-point ranges won’t get the collection method, so either >>>>>>> we’ll have inconsistent APIs (random FP value is non-optional, random >>>>>>> integer is optional) or we’ll make the FP API optional just to match. >>>>>>> Both of those seem bad. >>>>>>> >>>>>>>> One possible reason is if you exclude half-open ranges, only having >>>>>>>> CountableClosedRange, then you don’t have to account for the >>>>>>>> possibility of an empty collection (via an optional or a trap) because >>>>>>>> they cannot be empty. But closed ranges aren’t the currency type – >>>>>>>> half-open ranges are. So it’d hit usability if you have to convert >>>>>>>> from one to t'other often. >>>>>>>> >>>>>>>> Other possibility is discovery. But given the common use case is >>>>>>>> “random element from collection”, I don’t expect this to be an issue >>>>>>>> as it will quickly become common knowledge that this feature is >>>>>>>> available. >>>>>>> >>>>>>> Agreed here—I don’t think discovery is really an issue between the two >>>>>>> kinds. However, I don’t think the overlap in features (two ways to >>>>>>> generate random integers) are a problem, especially as we’d have better >>>>>>> alignment between integer and floating-point methods. >>>>>>> >>>>>>> Nate >>>>>>> _______________________________________________ >>>>>>> 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 <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