Swift 3 Ranges

I have the impression we exchanged flexibility for correctness (the

ability to represent 0..<Int.max) and that it's wasn't worth the loss
of flexibility.1

Or am I missing something?

Hi people,

I’ve recently started migrating some Swift 2 projects to Swift 3. I
came across the split of Range into Range and ClosedRange and I’ve
really struggled with it. Specifically, in Swift 2, I had a struct
with a Range property that was initialised in many places with
either a closed or open range:

struct Day { … }
struct Day : Comparable { … }
struct Day : Strippable { … }

struct Info {
    let name: String
    let range: Range<Day>
}

Info(name: "Christmas Vacation", range: twentyfith...thirtyfirst)
Info(name: "Summer Vacation", range: someday..<otherday)

Now, in Swift 3, it seems like we’ve lost a type to represent any
range to allow an API client the flexibility to specify it as he
wishes. Is there a solution to this problem through a protocol
which both ranges conform to, or are we stuck with this because of
the new API?

protocol RangeType {
    associatedtype Bounds
    let lowerBound: Bound { get }
    let upperBound: Bound { get }
// what else? not even sure if it is possible to define such a

protocol

}

David.

The problem was that to implement both generically, a closed range was
just an open range with the upper bound increment by one step, but as
you say this wasn't safe as Int.max etc. could not be used.

It was safe. Trapping is safe behavior. It was simply not expressive
enough to express the ranges people wanted to write, e.g. 0...Int8.max.
Your scheme (below) isn't either.

I think if you wanted to solve this you'd need to reintroduce the
concept of incrementing by a minimum step, which was moved away from
index types during the changes to the new indexing model for
collections. You could do this something like so:

// Reintroduce these methods that were lost in the new indexing API
protocol ForwardStep { func successor()? -> Self }
protocol BackwardStep : ForwardStep { func predecessor()? -> Self }

// Enable conversion of ranges
extend ClosedRange where Self.Bound : ForwardStep {
  func toOpenRange() -> Range<Self.Bound>? {
    guard let upperBound = self.upperBound.successor()
else { return nil }
    return self.lowerBound ..< upperBound
  }
}
extend Range where Self.Bound : BackwardStep {
  func toClosedRange() -> ClosedRange<Self.Bound>? {
    guard let upperBound = self.upperBound.predecessor()
else { return nil }
    return self.lowerBound ... upperBound
  }
}
extend ClosedRange where Self.Bound : BackwardStep {
  init(_ openRange:Range<Self.Bound>)? {
    guard let closedRange = openRange.toClosedRange() else
{ return nil }
    self = closedRange
  }
}
extend Range where Self.Bound : ForwardStep {
  init(_ closedRange:Range<Self.Bound>)? {
    guard let openRange = closedRange.toClosedRange() else
{ return nil }
    self = openRange
  }
}

This would not result in correct range conversion with respect to all
collections. For example, a StridedCollection might step its
ForwardStep indices several times for each call to index(after:).

···

on Tue Sep 06 2016, Haravikk <swift-evolution@swift.org> wrote:

On 6 Sep 2016, at 07:39, David Hart via swift-evolution > <swift-evolution@swift.org> wrote:
On 6 Sep 2016, at 08:15, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

--
-Dave