[swift-evolution] [Proposal] Safer half-open range operator

Vladimir.S svabox at gmail.com
Wed Apr 13 13:37:53 CDT 2016


On 13.04.2016 19:59, Pyry Jahkola wrote:
>> On 13 Apr 2016, at 17:53, Luis Henrique B. Sousa <lshsousa at gmail.com
>> <mailto:lshsousa at gmail.com>> wrote:
>>
>> (…) I totally agree with @Vladimir that we could have a more clear and
>> /swift-ly/ way to concisely wrap those operations.
>>
>> The behaviour pointed out by him looks very nice and doable to me.
>>
>> a = [1,2,3]
>> a[-1..<6] - raises runtime error (right behavior by default, doesn't
>> affect existing code)
>> a[truncate: -1..<6] - produces [1,2,3] (the very behaviour I proposed
>> initially)
>> a[safe: -1..<6] - produces nil (i.e [T]?) (no runtime errors and makes it
>> easy to handle unexpected results)
>
> I don't feel strongly about this. Yes, if this were shorter to express, it
> would feel like /nicer/ design. But what Haravikk and Chris L. already said
> seems to me as /wiser/ design.


IMO it is not just nicer, it provides you with handy and explicit 
alternatives.

In some situations, you are checking bounds and you sure that if you 
calculated them incorrectly - error will be raised.
But sometimes, you don't need to check exact bounds, probably "take these 
values, or give me nil of no such".

I can compare this with Dictionary we have in Swift.
Can you say if it is "wise" to return Optional(T) when we calls 
dict[somekey] ? Probably it is wise to raise error if there is no such key? 
(to force us to check the key first).

The proposed subscript for special situations when you know you need 
exactly this behavior to make your code clear and readable and you fully 
controls code&error flow.

>
> (@Vladimir: Besides, I'm sure `.clamped(to:)` wasn't invented for this
> purpose but for doing interval arithmetic on ranges. It just happens to
> somewhat work here.)
>
> – Would this feature really provide a measurable benefit to developers?
> – Under which circumstances do you find yourself with a past-the-end upper
> bound such as 6 where `a.count == 3`?
> – …Let alone a /negative/ start index like `-1`?
>
> I find cases like these to be much more common in languages like Python and
> Ruby where e.g. `array[-2]` refers to the penultimate element. Swift
> doesn't seem to want to go there.
>

Each good feature will provide benefit to developers. For those, who work a 
lot with arrays/bounds/slices/copies this will provide measurable benefit, IMO.
Can we live without such improvement? Absolutely. Are there more important 
proposals? Yes. But will this make Swift more elegant, handy, clear? Yes, I 
believe.

As for "Under which circumstances.." questions. Well.. something like "get 
+- 5 elements(at max) with center in some index"

let a = [1,2,3,4,5,6]
let index = random(0..<a.count)
let result = a[truncate: index-5...index+5]

or "get 5 elements from index, if no 5 elements from index - return empty 
array"

let a = [1,2,3,4,5,6]
let index = random(0..<a.count)
if let result = a[safe: index..<index+5]  //[Int]?
  {return result}
else
  {return []}

Something like this.


> For the use cases I can think of, Swift 3—as the proposal currently goes
> <https://github.com/apple/swift/blob/swift-3-indexing-model/stdlib/public/core/Collection.swift#L724-L808>—already
> offers the following suitable methods:
>
>      array[bounds]
>      array.prefix(maxLength)// no precondition
>      array.prefix(upTo: index)
>      array.prefix(through: index)
>      array.dropLast(n)// no precondition
>      array.dropFirst(n)// no precondition
>      array.suffix(from: index)
>      array.suffix(maxLength)      // no precondition
>
> If these feel too clumsy to use, maybe we should focus on making them all
> more convenient. Ideally, that suggestion would apply to all `Collection`s.
>
> — Pyry
>

Yes, some variants can be covered by these methods:
array.prefix(maxLength) -> array[truncate: 0..<maxLength]
array.prefix(upTo) -> array[0..<upTo]
array.prefix(through) -> array[0...through]

And some has no good alternatives in subscription:
array.suffix(from: index) -> array[truncate: index...Int.max ??
array.suffix(maxLength)   -> array[truncate: hm..

But. As array[bound] returns a copy(slice) of array, it feels natural to 
have variants of behavior of array[bound], just like you have different 
variants of prefix() methods.

So it seems for me like this proposal for subscript variants(safe: & 
truncate:) is good addition to then proposal you pointed to.


More information about the swift-evolution mailing list