Aside: `indices` being irregular can be a benefit in the context of 
auto-complete.

>       * What is your evaluation of the proposal?

+1, very much.

As a change from the current model, it’s an across-the-board improvement for 
me, at least.

In a bigger-picture sense I think Swift would be better off by going *further* 
on certain aspects, but have said all that before.

>       * Is the problem being addressed significant enough to warrant a change 
> to Swift?

It is, again very much so.

>       * Does this proposal fit well with the feel and direction of Swift?

Depends on the framing of the question.

Compared to the previous model, it’s an unqualified YES.

As a general proposition, I think this design is a local optimum for overall 
Swift-ness, but even so it’s creating a little un-Swifty pocket. It’s 
“un-Swifty” in at least two ways:

# 1: Relatively Unsafe, Pointer-Like Semantics

Indices—unsurprisingly!—behave quite a bit like pointers, and similarly expose 
*numerous* crashing combinations of `(value,operation)`:

- self[endIndex]
- self[startIndex] // <- when empty
- successor(of: endIndex)
- predecessor(of: startIndex)

…etc., which is *very much* reminiscent of the hazards of pointers. 
(Technically “undefined” not “crashing”, but being realistic “crashing" is 
usually accurate).

Although Swift uses `Optional` to mitigate the hazards of `nil` pointers 
(etc.), you’re still left to your own devices for handling indices.

This isn’t news to anyone here, I’m sure, and may even be unavoidable; I’m just 
pointing it out as an uncharacteristically-unsafe area in Swift’s standard 
APIs, and closer to how `!` and IOUs behave than otherwise typical.

To help illustrate the claim, here’s a strawman “safe” API—for illustration 
only, not advocacy!—that would be safer and thus perhaps more “Swift-y”:

  protocol SafeCollection {

    /// `Index` by another name
    associatedtype Position: Equatable // , Comparable too if you want
  
    /// Returns `nil` when empty. A non-strawman version
    /// would return something better than a labeled tuple, here.
    var bounds: (first: Position, last: Position)? { get }
  
    /// Returns the first position, or `nil` when empty. 
    /// Like `startIndex` but any non-nil value is safe to subscript.
    var firstPosition: Position? { get }

    /// Returns the last position, or `nil` when empty. 
    /// Like `endIndex` but any non-nil value is safe to subscript.
    var lastPosition: Position? { get }
  
    /// No risk of trying to subscript `endIndex` here.
    subscript(position: Position) -> Element
  
    /// Safe to call on any non-nil position from this collection;
    /// no risk of trying to advance the `endIndex` here.
    func successor(of p: Position) -> Position?

    /// Safe to call on any non-nil position from this collection;
    /// no risk of trying to retreat-from the `startIndex` here.
    func predecessor(of p: Position) -> Position?

    // and so on and so forth

  }

…which, again, I include for purpose of *comparison* only. I *believe* this 
illustration has more of the “index-safety” Brent is getting at, but I can’t 
speak for him.

Pros:
- safer, b/c there’s no `nil`-analog like `endIndex` that needs checking-for
- feels more Swift-like (safer, harder to mis-use)

Cons:
- likely less-efficient than current proposal
- wants you to use closed index ranges, not half-open
- doesn’t address index-invalidation (discussed immediately below)

So, again, I’m not advocating that “safer” design be Swift's basic 
building-block, but I think it illustrates why it’s fair to call the index 
system relatively “unsafe”: as proposed, indices have semantics that are 
lower-level than they seem, with a safety profile *closer to* IOU/`!` than most 
of Swift’s standard library. 

# 2: Index Invalidation Unrepresented In Type System 

A related “un-Swift-y” aspect is that index-invalidation has no type-level 
representation whatsoever. Some operations on collections invalidate indices, 
but which operations? Which indices?

I don’t have a proposed *solution* I feel good about as to how to represent 
invalidation in the type system, but even so “values that become invalid for 
further use due to actions that happen elsewhere” is not very Swift-y.

# Remarks

Don’t get me wrong, I still like this proposal, and am *very* skeptical that 
something like the “safer index” sketched above would be a net win; I just 
think it’s very fair to say that indices are a corner of the standard library 
that isn’t all that Swifty due to the lack of statically-enforced safety as per 
usual in the rest of the library.

>       * If you have you used other languages or libraries with a similar 
> feature, how do you feel that this proposal compares to those?

This is a tricky question in this context b/c pretty much every non-toy 
language with a non-toy standard library will have its own take on 
collections-and-iteration; it also makes it very hard to be concise, as the 
same terms mean slightly different things from language to language.

I think it’s fair to say that although *many* languages have something 
analogous to Swift’s `Generator` (soon: `Iterator`), *very few* languages have 
based their fundamental collections hierarchy/API around Swift-style indices.

Additionally, most languages I’m familiar with have a hierarchy that, in Swift 
terms, would look something like this:

- Sequence
  - Collection
    - ArrayCollection // or `ForwardCollection` in Swift, probably
    - DictionaryCollection // or AssociativeCollection, or MapCollection, etc.
    - SetCollection 

…as opposed to Swift’s design (wherein `Collection` *is* essentially the 
nonexistent `ForwardCollection`, and all collections are thus 
`ForwardCollection`s).

I’m not sure Swift gains much from being this way; put differently, if Swift 
currently looked like this:

  protocol CloningIterator : Iterator {

    /// Returns a copy of `self` (at the current iteration state).
    func clone() -> Self

  }

  protocol Collection : Sequence {
    // no `Index` here
    associatedtype Iterator: CloningIterator
    
    // minimal API here
    var isEmpty: Bool { get }
    var count: Int { get }
  }

  protocol ForwardCollection : Collection {
 
    // exactly as-per proposal’s `Collection`:
    associatedtype Index 
     
    // all Index-related methods exactly as-per proposal’s `Collection`

  }

  struct Array<T> : ForwardCollection {}
  struct Set<T> : Collection {}
  struct Dictionary<K,V> : Collection {}

…then I’d still want `ForwardCollection` to have an API like the proposal 
(wherein collections-move-indices), but I’d personally see no real value gained 
by “promoting" `Set` and `Dictionary` to be `ForwardCollection`.

But, I’ve made that case before.

I want to reiterate that I think this proposal is more-or-less the best 
possible design for what it is—collections *should* move indices, when they 
have them!

>       * How much effort did you put into your review? A glance, a quick 
> reading, or an in-depth study?

I’ve groused unproductively about aspects of iteration-and-collection design in 
numerous previous discussions; I’ve written some custom collections and some 
collection-combinators and in so doing encountered many issues with the 
previous design.

> 
> More information about the Swift evolution process is available at
> 
>       https://github.com/apple/swift-evolution/blob/master/process.md 
> <https://github.com/apple/swift-evolution/blob/master/process.md>
> 
> Thank you,
> 
> -Chris Lattner
> Review Manager
> 
> 
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to