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

Reply via email to