Range and ClosedRange


(David Hart) #1

Hello,

Since the release of Swift 3, I’ve seen quite a few people (me included) experience a lot of friction with the new types for representing ranges. I’ve seen people confused when writing an API that takes a Range as argument but then can’t pass in a ClosedRange. Sometimes this can be fixed because the API should be written against a more general protocol, but sometimes that’s not the case.

Those new types definitely seem to cause more problems than they fixed (the Int.max problem). Has the Standard Library team put any thought into this?

Regards,
David.


(Xiaodi Wu) #2

I agree that there are use cases where the distinction between ClosedRange
and Range is less ergonomic than perhaps possible. However, I would be very
much against rolling back the improved correctness, but as has been
suggested by a previous comment on the swift-users lists, a protocol would
not be out of place. By current Swift conventions, and given our goal of as
much source compatibility as possible (i.e. not renaming `Range`), such a
protocol would be named `RangeProtocol`.

At the moment, we actually have four range types: Range, CountableRange,
ClosedRange, CountableClosedRange. Once conditional conformances fall into
place, the Countable versions can go away. There are useful algorithms that
can ignore the distinction between an open range and a closed range when
the bounds are countable (e.g. Int) that do _not_ make sense when the
bounds are not countable (e.g. Float). This comes down to the fact that
`0...(2 as Int)` is in many ways another way of expressing `0..<(3 as Int)`
but `0...(2 as Float)` is very much not the same thing as `0..<(3 as
Float)`. Therefore, IMO it'd be wisest to hold off on designing a
`RangeProtocol` until the requisite generics features are implemented, so
that the final design can take advantage of such features for a maximally
ergonomic but still correct design.

···

On Thu, Jan 12, 2017 at 3:44 PM, Adriano Ferreira via swift-evolution < swift-evolution@swift.org> wrote:

Hi David,

There were a few instances where this topic or similar came up at the Swift
Evolution List
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160905/026923.html>
and Swift Users List
<https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20161010/003631.html>
.

There’s even this interesting proposal
<https://github.com/luish/swift-evolution/blob/f99e325484a5b19ce4bab8eda18f284e4e250e7b/proposals/nnnn-more-lenient-collections-subscripts.md> that
dwells with it while providing more lenient subscripts to collections.

BTW, I agree with you, having the range type split is somewhat confusing,
specially for those new to the language.

Best,

—A

On Jan 12, 2017, at 3:11 PM, David Hart via swift-evolution < > swift-evolution@swift.org> wrote:

Hello,

Since the release of Swift 3, I’ve seen quite a few people (me included)
experience a lot of friction with the new types for representing ranges.
I’ve seen people confused when writing an API that takes a Range as
argument but then can’t pass in a ClosedRange. Sometimes this can be fixed
because the API should be written against a more general protocol, but
sometimes that’s not the case.

Those new types definitely seem to cause more problems than they fixed
(the Int.max problem). Has the Standard Library team put any thought into
this?

Regards,
David.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Sean Heber) #3

I would very much like to know what the status is of conditional conformances and if there’s a timeline for them or they are underway or whatnot. My unscientific gut check suggests something like 50% of recent proposals or rough ideas have been impaired by their absence. :stuck_out_tongue:

l8r
Sean

···

On Jan 12, 2017, at 4:03 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

I agree that there are use cases where the distinction between ClosedRange and Range is less ergonomic than perhaps possible. However, I would be very much against rolling back the improved correctness, but as has been suggested by a previous comment on the swift-users lists, a protocol would not be out of place. By current Swift conventions, and given our goal of as much source compatibility as possible (i.e. not renaming `Range`), such a protocol would be named `RangeProtocol`.

At the moment, we actually have four range types: Range, CountableRange, ClosedRange, CountableClosedRange. Once conditional conformances fall into place, the Countable versions can go away. There are useful algorithms that can ignore the distinction between an open range and a closed range when the bounds are countable (e.g. Int) that do _not_ make sense when the bounds are not countable (e.g. Float). This comes down to the fact that `0...(2 as Int)` is in many ways another way of expressing `0..<(3 as Int)` but `0...(2 as Float)` is very much not the same thing as `0..<(3 as Float)`. Therefore, IMO it'd be wisest to hold off on designing a `RangeProtocol` until the requisite generics features are implemented, so that the final design can take advantage of such features for a maximally ergonomic but still correct design.

On Thu, Jan 12, 2017 at 3:44 PM, Adriano Ferreira via swift-evolution <swift-evolution@swift.org> wrote:
Hi David,

There were a few instances where this topic or similar came up at the Swift Evolution List and Swift Users List.

There’s even this interesting proposal that dwells with it while providing more lenient subscripts to collections.

BTW, I agree with you, having the range type split is somewhat confusing, specially for those new to the language.

Best,

—A

On Jan 12, 2017, at 3:11 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

Since the release of Swift 3, I’ve seen quite a few people (me included) experience a lot of friction with the new types for representing ranges. I’ve seen people confused when writing an API that takes a Range as argument but then can’t pass in a ClosedRange. Sometimes this can be fixed because the API should be written against a more general protocol, but sometimes that’s not the case.

Those new types definitely seem to cause more problems than they fixed (the Int.max problem). Has the Standard Library team put any thought into this?

Regards,
David.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adriano Ferreira) #4

Hi David,

There were a few instances where this topic or similar came up at the Swift Evolution List <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160905/026923.html> and Swift Users List <https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20161010/003631.html>.

There’s even this interesting proposal <https://github.com/luish/swift-evolution/blob/f99e325484a5b19ce4bab8eda18f284e4e250e7b/proposals/nnnn-more-lenient-collections-subscripts.md> that dwells with it while providing more lenient subscripts to collections.

BTW, I agree with you, having the range type split is somewhat confusing, specially for those new to the language.

Best,

—A

···

On Jan 12, 2017, at 3:11 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

Since the release of Swift 3, I’ve seen quite a few people (me included) experience a lot of friction with the new types for representing ranges. I’ve seen people confused when writing an API that takes a Range as argument but then can’t pass in a ClosedRange. Sometimes this can be fixed because the API should be written against a more general protocol, but sometimes that’s not the case.

Those new types definitely seem to cause more problems than they fixed (the Int.max problem). Has the Standard Library team put any thought into this?

Regards,
David.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Sweeris) #5

Do you mean that you think having two types is confusing, or that the way we currently split them is confusing?

If it's the former, then I disagree... IIRC, open vs closed ranges is covered in high school math, and IMHO it's not too hard to see the usefulness of both "0..<5" and "1...5".

If it's the latter, I think it's only confusing because, well, partly because we only implement half the kinds of ranges ("lower" is always closed, but that's another thread), but mostly because we don't have generic protocols yet. If we could write
    protocol Range<T> where T : WhateverTheCurrentConstraintsAre {
        var lower: T {get}
        var upper: T {get}
    }
Then we could define the concrete types as
    struct CCRange<T>: Range<T> {...}
    struct CORange<T>: Range<T> {...}
    struct OCRange<T>: Range<T> {...}
    struct OORange<T>: Range<T> {...}
(Or spell it out, "ClosedClosedRange", if you don't like the abbreviations.) Then in code, since `Range` doesn't have any "Self or associated type requirements", you can make just make it the type of a variable. In fact, if I'm not mistaken, the "..<" and "..." operators could even return a `Range` instead of the relevant concrete type
    var x = 0..<5 // "..<" returns `CORange as Range`
    x = 0...4 // "..." returns `CCRange as Range`, which is fine because x's type is `Range`, not `HalfOpenRange` (or whatever it's called now)
We'd get full polymorphism between all the types of range without losing the value semantics we want, and the current method of overloading functions is still available if you need the speed of static dispatch.

- Dave Sweeris

···

On Jan 12, 2017, at 15:44, Adriano Ferreira via swift-evolution <swift-evolution@swift.org> wrote:

BTW, I agree with you, having the range type split is somewhat confusing, specially for those new to the language.


(Adriano Ferreira) #6

Hi Dave,

I’m not arguing about correctness cause I understand the reasoning behind it, but about the abstraction itself.

I believe a simplified interface would be more fun to use and less confusing. This post <https://oleb.net/blog/2016/10/generic-range-algorithms/> from Ole Begemann talks a little bit about this interesting idea.

Best,

—A

···

On Jan 12, 2017, at 5:55 PM, David Sweeris <davesweeris@mac.com> wrote:

On Jan 12, 2017, at 15:44, Adriano Ferreira via swift-evolution <swift-evolution@swift.org> wrote:

BTW, I agree with you, having the range type split is somewhat confusing, specially for those new to the language.

Do you mean that you think having two types is confusing, or that the way we currently split them is confusing?

If it's the former, then I disagree... IIRC, open vs closed ranges is covered in high school math, and IMHO it's not too hard to see the usefulness of both "0..<5" and "1...5".

If it's the latter, I think it's only confusing because, well, partly because we only implement half the kinds of ranges ("lower" is always closed, but that's another thread), but mostly because we don't have generic protocols yet. If we could write
   protocol Range<T> where T : WhateverTheCurrentConstraintsAre {
       var lower: T {get}
       var upper: T {get}
   }
Then we could define the concrete types as
   struct CCRange<T>: Range<T> {...}
   struct CORange<T>: Range<T> {...}
   struct OCRange<T>: Range<T> {...}
   struct OORange<T>: Range<T> {...}
(Or spell it out, "ClosedClosedRange", if you don't like the abbreviations.) Then in code, since `Range` doesn't have any "Self or associated type requirements", you can make just make it the type of a variable. In fact, if I'm not mistaken, the "..<" and "..." operators could even return a `Range` instead of the relevant concrete type
   var x = 0..<5 // "..<" returns `CORange as Range`
   x = 0...4 // "..." returns `CCRange as Range`, which is fine because x's type is `Range`, not `HalfOpenRange` (or whatever it's called now)
We'd get full polymorphism between all the types of range without losing the value semantics we want, and the current method of overloading functions is still available if you need the speed of static dispatch.

- Dave Sweeris


(Max Moiseev) #7

FWIW, the common RangeProtocol unifying both Range and ClosedRange existed for a while when the new collection indexing model was being implemented.

Here is the commit removing it: https://github.com/apple/swift/pull/2108/commits/8e886a3bdded61e266678704a13edce00a4a8867

Max

···

On Jan 12, 2017, at 12:11 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

Since the release of Swift 3, I’ve seen quite a few people (me included) experience a lot of friction with the new types for representing ranges. I’ve seen people confused when writing an API that takes a Range as argument but then can’t pass in a ClosedRange. Sometimes this can be fixed because the API should be written against a more general protocol, but sometimes that’s not the case.

Those new types definitely seem to cause more problems than they fixed (the Int.max problem). Has the Standard Library team put any thought into this?

Regards,
David.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Hart) #8

I read this great post. But it shows an example where the problem is circumvented because the algorithm can work on a more general protocol. But the problem still persists for algorithms which make sense for any countable range (open and closed).

···

On 13 Jan 2017, at 02:57, Adriano Ferreira <adriano.ferreira@me.com> wrote:

Hi Dave,

I’m not arguing about correctness cause I understand the reasoning behind it, but about the abstraction itself.

I believe a simplified interface would be more fun to use and less confusing. This post from Ole Begemann talks a little bit about this interesting idea.

Best,

—A

On Jan 12, 2017, at 5:55 PM, David Sweeris <davesweeris@mac.com> wrote:

On Jan 12, 2017, at 15:44, Adriano Ferreira via swift-evolution <swift-evolution@swift.org> wrote:

BTW, I agree with you, having the range type split is somewhat confusing, specially for those new to the language.

Do you mean that you think having two types is confusing, or that the way we currently split them is confusing?

If it's the former, then I disagree... IIRC, open vs closed ranges is covered in high school math, and IMHO it's not too hard to see the usefulness of both "0..<5" and "1...5".

If it's the latter, I think it's only confusing because, well, partly because we only implement half the kinds of ranges ("lower" is always closed, but that's another thread), but mostly because we don't have generic protocols yet. If we could write
   protocol Range<T> where T : WhateverTheCurrentConstraintsAre {
       var lower: T {get}
       var upper: T {get}
   }
Then we could define the concrete types as
   struct CCRange<T>: Range<T> {...}
   struct CORange<T>: Range<T> {...}
   struct OCRange<T>: Range<T> {...}
   struct OORange<T>: Range<T> {...}
(Or spell it out, "ClosedClosedRange", if you don't like the abbreviations.) Then in code, since `Range` doesn't have any "Self or associated type requirements", you can make just make it the type of a variable. In fact, if I'm not mistaken, the "..<" and "..." operators could even return a `Range` instead of the relevant concrete type
   var x = 0..<5 // "..<" returns `CORange as Range`
   x = 0...4 // "..." returns `CCRange as Range`, which is fine because x's type is `Range`, not `HalfOpenRange` (or whatever it's called now)
We'd get full polymorphism between all the types of range without losing the value semantics we want, and the current method of overloading functions is still available if you need the speed of static dispatch.

- Dave Sweeris


(Xiaodi Wu) #9

FWIW, the common RangeProtocol unifying both Range and ClosedRange existed
for a while when the new collection indexing model was being implemented.

Here is the commit removing it: https://github.com/apple/
swift/pull/2108/commits/8e886a3bdded61e266678704a13edce00a4a8867

Max

From the commit: "The RangeProtocol was a very weak and fragile abstraction

because it didn't specify the interpretation of the endpoints. To write a
non-trivial algorithm, one usually needed to consult that information."

As I argued (perhaps inarticulately) above, I think it may be worthwhile
revisiting the abstraction once conditional conformances become possible.
We can say more about the relationship between the endpoints of half-open
and closed _countable_ ranges than we can of uncountable ones.

···

On Fri, Jan 13, 2017 at 2:05 PM, Max Moiseev via swift-evolution < swift-evolution@swift.org> wrote:

On Jan 12, 2017, at 12:11 PM, David Hart via swift-evolution < > swift-evolution@swift.org> wrote:

Hello,

Since the release of Swift 3, I’ve seen quite a few people (me included)
experience a lot of friction with the new types for representing ranges.
I’ve seen people confused when writing an API that takes a Range as
argument but then can’t pass in a ClosedRange. Sometimes this can be fixed
because the API should be written against a more general protocol, but
sometimes that’s not the case.

Those new types definitely seem to cause more problems than they fixed
(the Int.max problem). Has the Standard Library team put any thought into
this?

Regards,
David.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #10

FWIW, the common RangeProtocol unifying both Range and ClosedRange existed
for a while when the new collection indexing model was being implemented.

Here is the commit removing it: https://github.com/apple/
swift/pull/2108/commits/8e886a3bdded61e266678704a13edce00a4a8867

Max

From the commit: "The RangeProtocol was a very weak and fragile abstraction
because it didn't specify the interpretation of the endpoints. To write a
non-trivial algorithm, one usually needed to consult that information."

As I argued (perhaps inarticulately) above, I think it may be worthwhile
revisiting the abstraction once conditional conformances become possible.
We can say more about the relationship between the endpoints of half-open
and closed _countable_ ranges than we can of uncountable ones.

Yes, here's my plan (pending approval by this list of course):

1. Get rid of Countable[Closed]Range and use conditional conformance
   (when we get it) to make [Closed]Range conform to Collection when
   appropriate.
  
   1a. typealias CountableRange<T> where ... = Range<T> for backward
   compatibility. Similarly for CountableClosedRange.
   
2. Implement
   https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md
   This is a good proposal, badly formatted (sorry!) It includes a
   protocol RangeExpression, to which the ranges conform. Making this
   work really cleanly depends on having generic subscripts.

···

on Fri Jan 13 2017, Xiaodi Wu <swift-evolution@swift.org> wrote:

On Fri, Jan 13, 2017 at 2:05 PM, Max Moiseev via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 12, 2017, at 12:11 PM, David Hart via swift-evolution < >> swift-evolution@swift.org> wrote:

Hello,

Since the release of Swift 3, I’ve seen quite a few people (me included)
experience a lot of friction with the new types for representing ranges.
I’ve seen people confused when writing an API that takes a Range as
argument but then can’t pass in a ClosedRange. Sometimes this can be fixed
because the API should be written against a more general protocol, but
sometimes that’s not the case.

Those new types definitely seem to cause more problems than they fixed
(the Int.max problem). Has the Standard Library team put any thought into
this?

Regards,
David.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave