[swift-evolution] Proposal: Add Safe Subquence Access Via subscript For ColloctionType

Daniel Duan daniel at duan.org
Mon Dec 14 17:42:44 CST 2015


IMHO, Swift could give users more help to reduce boundary related errors. Perhaps a more practical thing to have is  `array[clamp: x…y]`, which always returns a Subsequence.

Along that line, I realize “safe” may be conveying the wrong idea.

Right now, a user have to remember that `someArray[3]` may crash while they don’t need to worry about `someDictionary[3]` crashing, I guess what’s really missing is a throwing version of `subscript`, or one such that nil is returned for invalid indexes. 

As for negative index, I accept the performance argument. “sometimes hide bugs” is less convincing as “crash early in production”, depending on context, is not necessarily the best way a programmer error to manifest. (example: crashing is the worst user experience on iOS. Wrong set of data *sometimes* gets along with the view layer just fine). From personal experience with Python, most of the time it’s a really useful feature. 

Regardless, this proposal doesn’t change any existing behavior, negative index is allowed only for retrieve subsequence (where Self.index: RandomAccessIndexType, so that `count` is accessible at O(1)). But I understand the the idea as a whole is a bit out there :P

Best,
Daniel


> On Dec 14, 2015, at 1:54 PM, Jordan Rose <jordan_rose at apple.com> wrote:
> 
> Hi, Daniel. Thanks for bringing this up. May I ask where you would use a "safe" subscript? When are you performing a subscript where the bounds being…um, out-of-bound…is not a programmer error?
> 
> As for the second half of this, we deliberately decided to not make the subscript operator "smart" (handling negative indexes and such) because extra branches can do very bad things to performance, and because allowing negative indexes sometimes hide bugs. It's also not meaningful for collections whose indexes are not integers.
> 
> Best,
> Jordan
> 
>> On Dec 14, 2015, at 12:52, Daniel Duan via swift-evolution <swift-evolution at swift.org> wrote:
>> 
>> 
>> In CollectionType, a `Range` is accepted as the argument in a version of `subscript`, which returns a subsequence.
>> 
>>   [1,2,3,4][2...3] // [3, 4]
>> 
>> `subscript` raises a fatal error if the range is out of bound, which is really a side-effect from accessing an element with an out of bound index. This behavior forces users to check bounds beforehand. It has been serving us well.
>> 
>> I propose adding a new interface where user can recover from/defer out of bound error in this context. Here are two potential approaches.
>> 
>> Approach #1 is more conservative, we add a throwing version of `subscript`. It throws an error if the range is out of bound. We can give the range parameter a name for distinction, resulting usage would look like:
>> 
>>   do {
>>      let gimme = [1,2,3,4][safe: 2...4]
>>   } catch {
>>       recover()
>>   }
>> 
>> As an alternative, we can replace the original `subscript` with this version, breaking backward compatibilty.
>> 
>> Apporoach #2 is a really sweet syntax sugar. We add a new subscript that accepts 2 arugments:
>> 
>>   extension CollectionType where Self.Index: RandomAccessIndexType {
>>       public subscript(start:Int?, end:Int?) -> Self.SubSequence { ... }
>>   }
>> 
>> This version would make ANY combination of arugment safe by enabling a sematic similar to Python's slicing mechanism. Explanations come after these examples:
>> 
>>   [0,1,2,3][1, -1]                    // [1, 2]
>>   ["H","e","l","l","o"][-1000, nil]   // ["H","e","l","l","o"]
>>   [1,2,3,4,5,6,7,8][1,5][2,3]         // [4]
>> 
>> This should look familiar to Python users:
>> 
>> * the access is always clamped in-bound. Interpret out-of-bound number as the boundary.  [1,2,3][0: 100] means [1,2,3][0: 2].
>> * nil indicate the boundary itself. [1,2,3][0: nil] means [1,2,3][0: 2]
>> * negative index counts from the end of the sequence. [1,2,3][-2, -1] means [1,2,3][(3-2), (3-1)]
>> 
>> Admittedly, this syntax suger seems a little out-of-place in Swift :P
>> 
>> Both approaches require just a little of work. As an example, here's one implementation of the 2nd: https://github.com/dduan/Lic/blob/master/Lic/Lic.swift (please ignore the extension for String, that'd be in a separate proposal, if any).
>> 
>> 
>> What do you think?
>> 
>> - Daniel Duan
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 



More information about the swift-evolution mailing list