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

Luis Henrique B. Sousa lshsousa at gmail.com
Wed Apr 13 14:57:17 CDT 2016


Another common application would be on pagination systems. For example,
this function that I just found randomly:
https://github.com/MrAlek/PagedArray/blob/bf64cbb140cf8bd109483dd749ac40a5f4531dfd/Source/PagedArray.swift#L88


public func indexes(pageIndex: Int) -> Range<Index> {
    assert(pageIndex >= startPageIndex && pageIndex <= lastPageIndex, "Page
index out of bounds")

    let startIndex: Index = (pageIndex-startPageIndex)*pageSize
    let endIndex: Index
    if pageIndex == lastPageIndex {
        endIndex = count
    } else {
        endIndex = startIndex+pageSize
    }

    return (startIndex..<endIndex)
}


wouldn't be required anymore before accessing the array, having simply
something like

> let current = pageIndex * pageSize
> array[safe: (current - pageSize) ..< (current + pageSize)]

(or *truncate* according to the user expectation) instead, which in my
opinion looks much more elegant and handy.


Regards,

- Luis

On Wed, Apr 13, 2016 at 7:37 PM, Vladimir.S via swift-evolution <
swift-evolution at swift.org> wrote:

>
> 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.
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160413/1c9a6601/attachment.html>


More information about the swift-evolution mailing list