[swift-evolution] Feature proposal: Range operator with step

Brent Royal-Gordon brent at architechies.com
Thu Apr 7 16:24:54 CDT 2016

>> Actually, it would need to be something like
>> `calendar[startDate..<endDate, unit: .Day].striding(by: 1)`, because
>> NSCalendarUnit is not itself a stride, it is the *unit* of the
>> stride. 
> Maybe:
>  calendar.days[startDate..<endDate].striding(by: 1)

I've been leaning towards parameters for additional flexibility, but the difference there is only slightly more than bikeshedding.

> However, it doesn't explain why
>  calendar[startDate..<endDate, unit: .Day].striding(by: 1)
> “doesn't quite help.”  It seems to me that 
>  calendar[startDate..<endDate, unit: .Day]
> does factor in the calendar and unit.

Yes, it does—as long as Strideable changes so that the instance created by this expression participates in the striding. My point is that, as long as the operations in Strideable are performed on one of the two strides, the "range" we're moving over can't be involved enough to help. Striding has to be done with the assistance of the instance owning the start and end, not just the start and end themselves.

Basically, what I'm saying is that Strideable needs a redesign along the lines of the indexing system. We redesigned indexing so that the collection manipulates the indexes, rather than the indexes manipulating themselves, because the indexes don't always have enough information to do the job. We need to do the same thing with Strideable.

>> For example, suppose you modify `Strideable` so that, instead of
>> applying to the values participating in the striding, it applies to
>> the instance containing those values:
>> 	public protocol Strideable {
>> 		associatedtype Element: Comparable
>> 		associatedtype Stride: SignedNumber
>> 		var start: Value
>> 		var end: Value
>> 		public func distance(from earlier: Element, to later: Element) -> Stride
>> 		public func advance(element: Element, by stride: Stride) -> Element
>> 	}
> Presumably you mean for Strideable to have a striding(by:_) method as well?

Yes, there would be an extension method like that (or there would be a `func stride<StrideableType: Strideable>(over: StrideableType, by: StrideableType.Stride) -> …`, which is the same thing). I didn't include it because it's not a requirement imposed on the conforming type.

> If so, how is this fundamentally different from Collection?  Shouldn't
> every Collection support this?

Huh, interesting. Very, very interesting.

Strideable is more widely applicable than Collection; for instance, a Range<Double> can't be a Collection (except via `nextafter`), but it is Strideable. But every RandomAccessCollection can be Strideable. (So could any BidirectionalCollection, for that matter, although it would be slower.) `array.striding(by: 2)` is a coherent and useful operation.

So yes, I think every Collection (with a suitable index) could be Strideable. But there are Strideable things which aren't collections.

> Except we don't have that capability today.  Instead we'd be using
> overloads of ..< and ... to produce more-capable range types, c.f.  
> https://github.com/apple/swift/blob/swift-3-indexing-model/stdlib/public/core/Range.swift#L504
> https://github.com/apple/swift/blob/swift-3-indexing-model/stdlib/public/core/Range.swift#L519

That works too, I suppose. You would end up with a StrideableRange "between" your current CountableRange (which is a Collection) and Range (which is a glorified tuple).

Brent Royal-Gordon

More information about the swift-evolution mailing list