[swift-evolution] [Review] SE-0065 A New Model for Collections and Indices

Brent Royal-Gordon brent at architechies.com
Mon Apr 11 00:42:25 CDT 2016

> 	https://github.com/apple/swift-evolution/blob/master/proposals/0065-collections-move-indices.md

Some questions and comments:

> 		• Two for ranges that additionally conform to RandomAccessCollection (requiring bounds that are Strideablewith Stride conforming to Integer): CountableRange<T> and CountableClosedRange<T>. These types can be folded into Range and ClosedRange when Swift acquires conditional conformance capability.

Does this mean that once we have conditional conformances, HalfOpenRangeProtocol and ClosedRangeProtocol will most likely go away?

> We also introduce three new protocols:
> 	• RangeProtocol
> 	• HalfOpenRangeProtocol
> 	• ClosedRangeProtocol
> These protocols mostly exist facilitate implementation-sharing among the range types, and would seldom need to be implemented outside the standard library.

If these types are for implementation sharing, should they be underscored to discourage their use? Or is the position they occupy in the type hierarchy important because Range and ClosedRange will eventually occupy them?

It seems to me that RangeProtocol is unlike the other two in that it's a sensible thing to constrain a parameter to. Does the "implementation-sharing" comment not really apply to it? On the other hand, it's not like the SubSequence subscript now takes a RangeProtocol. Should it?

(Has any thought been given to allowing you to close protocols to outside conformances the way that resilience seems to suggest we'll eventually allow you to close classes?)

> • Two for general ranges (whose bounds are Comparable): Range<T> and ClosedRange<T>. Having a separate ClosedRange type allows us to address the vexing inability of the old Range to represent a range containing the maximal value of its bound.

I notice that ClosedRange uses a ClosedRangeIndex to, essentially, add one extra value for "larger than all values of this type". Could this solution be applied to Range to allow us to unify Range and ClosedRange, or are there other obstacles to that?

(There likely are. I just wanted to make sure the idea wasn't left unexplored.)

> func successor(of i: Index) -> Index

Two things:

1. I would really like a version of this which returns Optional and is guaranteed to go `nil` once it hits `endIndex`. There can be a non-optional version too, or it might even be a feature of the `index` family of methods instead of `successor` itself, but I think it would be valuable to let the collection worry about the bounds check. It seems silly to check the index before calling `successor(of:)` when `successor(of:)` is going to immediately perform the same check again as a precondition.

(Actually, in general, I'm a little bit dismayed that the collection API does so little to help you include bounds checks in your code. Especially when iterating through a collection, bounds checks are absolutely mandatory, and the collection API's solution to the problem is "eh, just use `<`, it's not like you might mess something like that up".)

2. There is a very strong parallel between this method and a generator's `next()` method—the generator's `next()` calls `successor(of:)` to get the index it should use—which makes me wonder if this method should also be called `next(_:)` instead of `successor(of:)`. On the other hand, I have no good suggestion for a 

> func index(n: IndexDistance, stepsFrom i: Index) -> Index

Oof, I am really not a fan of this name. `steps` is sort-of a label on the `n` parameter, but it's attached to `i`. Other collection APIs use `distance`, not `steps` (although "steps" does appear in the documentation of the `Distance` associated type). `index` puts it in a method family with `index(predicate:)` and `index(of:)`, but those two are user-facing while this one is part of the collection API. Even the word `index` itself is redundant with the method return type.

I do understand how this is kind of parallel to `index(of:)` and `index(predicate:)`, in that they all return an index calculated from the parameters, but I think these methods are more different than they are similar.

Compared to this:

	collection.index(5, stepsFrom: i)

I would prefer any of these (ordered from least favorite to most):

	collection.index(distance: 5, from: i)
	collection.index(5, from: i)
	collection.traveling(5, from: i)
	collection.striding(5, from: i)
	collection.advancing(i, by: 5)

A word on `striding(_:from:)` appearing in that list: Although redesigning Strideable is not directly in scope for this proposal, I've noticed that our discussions on modernizing Strideable seem to be trending towards the idea that it operates on collections (or rather, on an as-yet-unnamed supertype of `BidirectionalCollection` or `RandomAccessCollection`) and strides by repeatedly calling a method with the same semantics as this one. Thus, there seems to be an intimate connection between this operation and Strideable. I think we ought to choose a method name which suits that, and I don't think `index` is it.

> func index(n: IndexDistance, stepsFrom i: Index, limitedBy limit: Index) -> Index

I have a few issues with this API.

1. As aforementioned, I'm not a big fan of using `index` as the base method name.

2. This method can move the index either forwards or backwards, but only one limit is specified. Would we be better off having the `limit` be a range?

3. What is the use case for returning the `limit` instead of returning the fact that we crossed it? I have a hard time thinking of a case where I would want to just bump up against the limit and use it rather than *detect* that I've hit the limit (which would probably call for a return type of `Index?`). Are there common needs that I'm just not thinking of? Should we offer both?

> 	* What is your evaluation of the proposal?

Despite my criticisms, this is fundamentally a very good design. It will not only improve the language, it will also open the door to further improvements.

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

Yes. I believe this change is complicating in the short run but actually simplifying in the long run, eliminating concepts like the Index protocols which represented several overlapping semantics.

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


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

Nothing with a collection design as rich as Swift's.

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

Somewhere between the latter two. I wouldn't call it in-depth when it's such a big change, but I feel like I have too much background to say it's a quick reading, either.

Brent Royal-Gordon

More information about the swift-evolution mailing list