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

Dennis Lysenko dennis.s.lysenko at gmail.com
Mon Dec 14 17:40:22 CST 2015


Jordan, I think the inspiration here might come from Ruby. I must admit
that seeing that `Array.first` returns an optional, but Array#subscript
raises a runtime error when the index is out of bounds threw me for a loop.
In Ruby, both Array#first and Array.subscript return an optional.

If one of the original tenets of swift was to provide greater compile-time
null-safety, which it definitely seems it was given the commendable
emphasis on optionals being easy to use, then returning an optional would
be a solid way to go about subscripting. Think of it this way: when I call
a method with nullable return value, I am forced to deal with the fact that
the method can fail at compile time. When I subscript an array, I am not
forced to deal with it at compile time, and it will fail at runtime instead.

Nullable subscripting is a big departure from the way most modern languages
do things and that is why I don't blame you for rejecting it. That said, it
is a pleasant change in the way you think about subscripting.

As a closing thought, subscripting hashes returns an optional value. You
might consider this a pretty big inconsistency with arrays. Let me flip
your argument against optional array subscripting, for dictionaries: *When
you are performing a subscript where the key is out of the key set, is it
not a programmer error?*

On Mon, Dec 14, 2015 at 4:54 PM Jordan Rose via swift-evolution <
swift-evolution at swift.org> 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
>
> _______________________________________________
> 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/20151214/3af895f5/attachment.html>


More information about the swift-evolution mailing list