# Critic notes about Swift Range types jungle

**URL:** <https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317>\
**Category:** Discussion\
**Tags:** range\
**Created:** [July 23, 2023, 5:57pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317 "2023-07-23T17:57:58Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [July 23, 2023, 5:57pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/1 "2023-07-23T17:57:58Z")

</div>

_TL&DR:  
We have so many different and incompatible range types in Swift: Range, ClosedRange, PartialRangeFrom, PartialRangeThrough, PartialRangeUpTo, UnboundRange plus a RangeExpression protocol. Wouldn't it be better to have a single type instead?_

* * *

## Critic notes about Swift Range types jungle

1. There's a ClosedRange and Range (which is OpenRange). Perhaps OpenRange name would be better.

2. Technically they are half open ranges. HalfClosedRange

3. I can see a benefit of these two ranges being different:  
Double.random(in: 0 ... 1)  
Double.random(in: 0 ..\< 1)

4. Is 1 ..\< 9 equal to 1 ... 10? Practically speaking yes, but you can't compare them and their hashValues are different.

5. The concept of one sided ranges adds another type... Is "1 ..." equal to "1 ... .max" - again, for practical purposes yes, but those are different types that can't be compared.

6. A peculiar property about one sided ranges is that while you can use white space with normal ranges you can't with one side ranges (no space allowed for prefix / postfix operators)

7. UnboundedRange is the whole new thing... it's not even generic?!  
`UnboundedRange<Int> // Error`

8. Is unbound range of Int (if existed!) equal to .min ... .max ? Practically yes, but again they are different types and can't be compared.

9. As a developer implementing an algorithm that wants to be as general as possible I have to express the algorithm taking RangeExpression parameter instead of a concrete type, which is less convenient due to the need of support of not just one generic dimension (boundary type) but the other dimension (concrete range type I am dealing with):

```swift
func foo<T>(range: ClosedRange<T>) {
    print(range.upperBound)
}

vs

func foo<T, Range: RangeExpression<T>> (range: Range) {
    print(range.upperBound) // Error
    fatalError()
}

```

1. Other languages have a notion of "range type", which is a similar but different concept: say, a type 0 ... 10 would only accept values within this range, attempting to have a value of that type out of that range would be a runtime error. Swift totally lacks here and I'd welcome this addition to be in Swift one day.

* * *

Could we done ranges differently in Swift?

```swift
// no RangeExpression protocol

// This is a SINGLE range type takes care of all cases
struct Range<T>: Hashable {
    enum Openness { case `open`, close }
    struct Edge {
        var openness: Openness
        var value: T
    }
    var lower: Edge
    var upper: Edge
}

infix operator .>.. : RangeFormationPrecedence // †
infix operator .>.< : RangeFormationPrecedence

func ... <T: Comparable> (lhs: T, rhs: T) -> Range<T> { ... }
func ..< <T: Comparable> (lhs: T, rhs: T) -> Range<T> { ... }
func .>.. <T: Comparable> (lhs: T, rhs: T) -> Range<T> { ... }
func .>.< <T: Comparable> (lhs: T, rhs: T) -> Range<T> { ... }
// Option A. either no one sides ranges, use explicit constants like min / max / inf
// Option B. or similar to what we have now, returning the same Range type

```

```swift
(1 ... 9) == (1 ..< 10)
(1 ... 9) == (0 .>.. 9)
(1...) == (1 ... .max) // Option A: Error
(1...) == (1 ... .max) // Option B: true
(1.0...) == (1 ... .inf) // Option A: Error
(1.0...) == (1 ... .inf) // Option B: true

```

* * *

_† - using ".\>.." and ".\>.\<" name instead of simpler "\>.." and "\>.\<" names is a consequence of an unfortunate rule:_

> [@](#):
>
> If an operator doesn’t begin with a dot, it can’t contain a dot elsewhere.

---

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [July 23, 2023, 6:29pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/2 "2023-07-23T18:29:24Z")

</div>

I will say: I think we should replace `UnboundedRange`.

Sometimes I implement things which include `replaceSubrange`, but don't conform to `RangeReplaceableCollection` (for reasons that are not worth going in to now). So, to ensure I support half-open ranges and all those other nice convenient ranges, I implement the function as:

```swift
mutating func replaceSubrange(
  _ subrange: some RangeExpression<Index>,
  with newElements: some Sequence<Element>
) -> Range<Index>

```

This works with everything except `UnboundedRange`, which actually _cannot_ conform to `RangeExpression` since, as you noted, it [is not generic](https://github.com/apple/swift/blob/b4ee68bd37c4f7db6102dcc41b0cda5b50dc390a/stdlib/public/core/Range.swift#L878-L892) and therefore has nothing for the `Bound` associated type to witness.

Ugh.

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [July 23, 2023, 6:44pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/3 "2023-07-23T18:44:39Z")

</div>

> [@Karl](#):
>
> This works with everything except `UnboundedRange`

Is not replacing the entire unbounded “sub”-range with a sequence of new elements isomorphic to reassigning the value by calling the initializer that takes that sequence as its argument? In the general case, since there are collection types that check and enforce some invariant after mutation, calling `replaceSubrange` to perform this operation would be less than ideal. Is there some reason this specific example is coming to mind?

While I agree that there are specific examples where expressivity limitations cause `...` to be excluded where it does not make sense to be, this example gives me some pause as to whether in a sizable proportion of use cases, or even more often than not, the right thing is for the unbounded range case to be considered explicitly and handled separately, such that the current expressivity limitation is a feature and not just a bug.

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [July 23, 2023, 6:51pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/4 "2023-07-23T18:51:49Z")

</div>

> [@tera](#):
>
> ## Critic notes about Swift Range types jungle

Hi @tera,

A lot of these points are not new and have been discussed at length in the past. Indeed, some of your observations (e.g., about one-sided ranges) are not entirely accurate and a careful study of those discussions would have helped to clarify. I would encourage you to spend some time with the very large amount of knowledge that has been preserved on these forums (the whole point of switching from the mailing list was to make it possible and easy to do so) before prompting the community to rehash them here again. If after such study there are new insights about how the standard library can be improved which you’ve newly discovered that haven’t been considered in the past, it would be of course delightful to hear them.

> [@tera](#):
>
> We have so many different and incompatible range types in Swift: […]. Wouldn't it be better to have a single type instead?

I want to emphasize that it would take a deeper level of insight to move the needle on this particular point. It goes without saying that, if it would “be better” to have one range type instead of six, the core team and the folks doing the implementation work would not have opted for six range types: it is implausible that they just didn’t think of that option and need it pointed out. Now, if you want to undertake a deep study of all the reasons why six types were chosen instead of one, and then look for any reasons which applied back then but do not apply to Swift as it is now thanks to new language features, and you can demonstrate convincingly that reworking the standard library in an ABI- and API-compatible way using the new features results in quantifiable benefits to user code (performance, correctness, etc.), then by all means we should have that conversation. But the onus for that work has to be on you: the people who designed the range types explained their rationale to the satisfaction of the core team at the time their proposals were adopted—they’re not on standby for all eternity to explain their choices on these forums.

---

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [July 23, 2023, 6:57pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/5 "2023-07-23T18:57:05Z")

</div>

> [@xwu](#):
>
> Is not replacing the entire unbounded “sub”-range with a sequence of new elements isomorphic to reassigning the value by calling the initializer that takes that sequence as its argument?

Not if your type doesn't support initialising a value out of thin air, such as if it is a mutable "view" type. Indeed, one of the reasons why I opted not to conform to RRC was so that I could omit that initialiser.

In my case, I was doing it with URL path components. A user may very well want to set the entire path using a collection of components, replacing anything that currently exists. But they can't create a `PathComponents` view from nothing, as it needs to know which URL it is part of and have the appropriate backing storage to write to (the behaviour can actually change depending on the values of other URL components -- like everything URL-related, the details are more complex than anyone would believe).

> [@xwu](#):
>
> In the general case, since there are collection types that check and enforce some invariant after mutation, calling `replaceSubrange` to perform this operation would be less than ideal.

I don't see a problem with that. It is perfectly acceptable for a user to write:

```swift
url.pathComponents.replaceSubrange(
  url.pathComponents.startIndex...,
  with: ["foo", "bar", "baz"]
)

```

And we would need to deal with all invariant checks just the same as if they used a more compact spelling:

```swift
url.pathComponents.replaceSubrange(..., with: ["foo", "bar", "baz"])

```

The latter has the additional benefit that we don't call the `.pathComponents` getter twice.

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [July 23, 2023, 7:24pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/6 "2023-07-23T19:24:01Z")

</div>

> [@tera](#):
>
> ```swift
> // This is a SINGLE range type takes care of all cases
> struct Range<T>: Hashable {
> enum Openness { case `open`, close }
> struct Edge {
> var openness: Openness
> var value: T
> }
> var lower: Edge
> var upper: Edge
> }
> 
> ```

assuming the alignment is not too weird, this type will have a stride of `4 * MemoryLayout<T>.stride`. the existing ranges, which encode `Openness` in the type system, occupy half as much space in memory.

for what it’s worth, i don’t think the appeals to historical precedent hold water as arguments against simplifying the range model. but doubling the memory footprint of a currency type like `Range<T>` is clearly going to be a nonstarter.

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [July 23, 2023, 7:47pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/7 "2023-07-23T19:47:36Z")

</div>

In the list of swift/proposals looking for "range" I can see only SE-0172 which introduces a new RangeExpression and a few new types, but not the previous split in to Range / ClosedRange which probably predates Swift evolution process. Would love to read relevant old discussions – would take me a bit to find them so bear with me.

> [@taylorswift](#):
>
> assuming the alignment is not too weird, this type will have a stride of `4 * MemoryLayout<T>.stride`. the existing ranges, which encode `Openness` in the type system, occupy half as much space in memory.

Do you think range values are getting stored en mass, e.g. in Array of ranges?

The bullet point #10 from my list above could actually help here:

```
struct Edge {
    var value: T
    var openness: Openness
}

```

This could occupy 8 bytes for T being an interval of numbers from 0 to 2^55 (7 bytes) which is very big number sufficient for most if not all practical applications.

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [July 23, 2023, 7:54pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/8 "2023-07-23T19:54:26Z")

</div>

> [@tera](#):
>
> Do you think range values are getting stored en mass, e.g. in Array of ranges?

for any type in the standard library (well, maybe not `ContinuousClock`) if you ask the question “is it being stored en masse” the answer is _yes_, somewhere someone is storing it en masse.

probably not by itself, probably as a field in a larger struct, but yes.

> [@tera](#):
>
> The bullet point #10 from my list above could actually help here:
> 
> ```swift
> struct Edge {
> var value: T
> var openness: Openness
> }
> 
> ```
> 
> This could occupy 8 bytes for T being an interval of numbers from 0 to 2^55 (7 bytes) which is very big number sufficient for most if not all practical applications.

well, it would not work with `Int`, which has a size, stride, and alignment of 8, would it?

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [July 23, 2023, 8:41pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/9 "2023-07-23T20:41:35Z")

</div>

> [@taylorswift](#):
>
> for any type in the standard library (well, maybe not `ContinuousClock`) if you ask the question “is it being stored en masse” the answer is _yes_, somewhere someone is storing it en masse.

You mean `ContinuousClock.Instant`? What's wrong with it?  
Or do you just mean that people won't store it en masse because it's huge?

> [@taylorswift](#):
>
> well, it would not work with `Int`, which has a size, stride, and alignment of 8, would it?

Not an arbitrary `Int`, although huge numbers rarely needed as indexes or bounds (It's like an array subscript - for all practical applications it could be 32 bits). If you have a practical example of having index value greater than what could fit in 7 bytes please share.

As a convenience we could allow creating such ranges from `Int`:

```swift
enum Openness {
    case `open`, closed
}

typealias Int56 = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)

struct Edge {
    var value: Int56
    var openness: Openness
    
    init(value: Int, openness: Openness) throws {
        guard let value = Int56(value) else { throw EdgeError.tooBigInteger }
        self.value = value
        self.openness = openness
    }
}

```

---

<div class="post-metadata">

**Author:** ![aggie33](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/aggie33/32/29896_2.png) [@aggie33](https://forums.swift.org/u/aggie33)\
**Post date:** [July 23, 2023, 9:10pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/10 "2023-07-23T21:10:12Z")

</div>

I would say the two most common uses of ranges are:

1. For loops

```swift
for i in 1...100 { ... }

```

1. Collection subscripting

```swift
print([1, 2, 3][1...])

```

Neither of which are affected very much if the memory size of ranges increases. That said, this seems kind of unnecessary. This makes it harder to write simple algorithms on ranges, and is basically an `AnyRangeExpression` type. There are a lot of range types, but they all make sense.

I do think it would be good to update `UnboundedRange`. Currently, it's just a type alias for a function taking an `_UnboundedRange` and returning void, which can never be called since `_UnboundedRange` is an uninhabited enum. But once we gain the ability to extend function types to conform to protocols, `UnboundedRange` can actually conform to `RangeExpression`. Until then, we might want to make an actual, empty unbounded range type to use as an easy default argument to range expression parameters.

---

<div class="post-metadata">

**Author:** ![aggie33](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/aggie33/32/29896_2.png) [@aggie33](https://forums.swift.org/u/aggie33)\
**Post date:** [July 24, 2023, 2:23am UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/11 "2023-07-24T02:23:45Z")

</div>

Also, the option you propose only replaces `Range` and `ClosedRange`. It doesn't address `UnboundedRange`, `PartialRangeUpTo`, `PartialRangeThrough` and `PartialRangeFrom`.

```swift
- (.open, .close) - 1..<3 // Range<Bound> (1, 2)
- (.close, .close) - 1>.<3 // currently does not exist (2)
- (.open, .open) - 1...3 // ClosedRange<Bound> (1, 2, 3)
- (.close, .open) - 1>..3 // currently does not exist (2, 3)

```

Adding a third case to Openness, `unbounded`, would fix this.

```swift
- (.unbounded, .close) - ..<3 // PartialRangeUpTo<Bound> (Int.min, ..., 2)
- (.unbounded, .open) - ...3 // PartialRangeThrough<Bound> (Int.min, ..., 2, 3)
- (.close, .unbounded) 1>.. // currently does not exist (2, 3, ..., Int.max)
- (.open, .unbounded) 1... // PartialRangeFrom<Bound> (1, 2, 3, ..., Int.max)
- (.unbounded, .unbounded) ... // UnboundedRange (Int.min, ..., Int.max)
```

---

<div class="post-metadata">

**Author:** ![bbrk24](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bbrk24/32/27370_2.png) [@bbrk24](https://forums.swift.org/u/bbrk24)\
**Post date:** [July 24, 2023, 2:29pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/12 "2023-07-24T14:29:33Z")

</div>

> [@aggie33](#):
>
> `1>.<3 // currently does not exist (2)`

Note that under the current rules of the language, this _cannot_ exist. An operator that does not start with `.` may not contain it, and `>` may not be a unary postfix operator.

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [July 24, 2023, 3:08pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/13 "2023-07-24T15:08:55Z")

</div>

I found the concept of "unbounded" very strange. e.g. this range (pseudocode):

```swift
(UInt8(1) ... UInt8.unbounded)

```

doesn't feel right to me. Imho it is as good as a bounded range between 1 and 255, i.e. (1 ... max)

Even for Double it could be represented as a bounded range from 1 to +inf

> [@bbrk24](#):
>
> Note that under the current rules of the language, this _cannot_ exist. An operator that does not start with `.` may not contain it ...

I wonder if that was the very reason to not have "left open" ranges...

```swift
Double.random(in: 1 ... 2) // ✅
Double.random(in: 1 ..< 2) // ✅
Double.random(in: 1 >.. 2) // 🛑 oops
// But let's workaround: -Double.random(in: -2 ..< -1)
Double.random(in: 1 >.< 2) // 🛑 oops, no workaround other than manual filtering of "1.0" value

```

---

<div class="post-metadata">

**Author:** ![aggie33](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/aggie33/32/29896_2.png) [@aggie33](https://forums.swift.org/u/aggie33)\
**Post date:** [July 24, 2023, 4:20pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/14 "2023-07-24T16:20:46Z")

</div>

A range expression is usually either sugar for a collection or a comparison operation. If I'm pattern matching:

```swift
switch n {
 case 1...:
  print("one or over")
 default:
  print("under one")
}

// in your pitch:
switch n {
  case 1...Int.max:
    ...
  default:
    ...
}

```

That's basically equivalent to:

```swift
if n >= 1 { ... } else { ... }

```

So an unbounded range serves a useful role in pattern matching and collection subscripting. In your hypothetical example, one would have to write

```swift
let string = "Hello, world!"
let index = string.firstIndex(of: "w")

// swift now
print(string[index...])

// swift with your proposal
print(string[index..<string.endIndex])

```

So I think unbounded ranges do serve a useful purpose.

---

<div class="post-metadata">

**Author:** ![aggie33](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/aggie33/32/29896_2.png) [@aggie33](https://forums.swift.org/u/aggie33)\
**Post date:** [July 24, 2023, 4:23pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/15 "2023-07-24T16:23:00Z")

</div>

I think the range design was also influenced by what it was replacing. They wanted to replace these methods:

```swift
prefix(upTo:)
prefix(through:)
suffix(from:)

```

So they added three new range types to match them.

```swift
PartialRangeUpTo<Bound>
PartialRangeThrough<Bound>
PartialRangeFrom<Bound>

```

But the fact that a range operator not including the left bound would have to be spelled like this `.>..` probably influenced the decision.

---

<div class="post-metadata">

**Author:** ![QuinceyMorris](https://avatars.discourse-cdn.com/v4/letter/q/d26b3c/32.png) [@QuinceyMorris](https://forums.swift.org/u/QuinceyMorris)\
**Post date:** [July 24, 2023, 5:55pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/16 "2023-07-24T17:55:32Z")

</div>

> [@tera](#):
>
> I wonder if that was the very reason to not have "left open" ranges...

> [@tera](#):
>
> we can only have "half open vs closed" on the right end, left end is always closed. why?

Indexing is asymmetrical regarding left and right. To see this, think about inserting an element between elements 3 and 4 of an existing array of 5 `Int`s. The function to do this could be either "insert after 3" or "insert before 4", and it's a non-trivial difference because it means the index parameter to the function is different in each case.

Swift (like most languages) has opted for the "right handed" approach: it's "insert before 4".

As a consequence, the openness also most frequently belongs on the right-hand end of a range, since (to end the range between element 3 and 4) we typically want to use index 4 ("before"), not 3 ("after"), for consistency even though both would work.

That doesn't mean that "openness on the left" would somehow be bad. It's just not something that there seems to be a lot of demand for. By contrast, "openness on the right" fits naturally with constructs like `for index in 0 ..< array.count`, being concise and avoiding an off-by-one error.

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [July 24, 2023, 7:24pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/17 "2023-07-24T19:24:21Z")

</div>

> [@aggie33](#):
>
> ```swift
> // swift now
> print(string[index...])
> 
> // swift with your proposal
> print(string[index..<string.endIndex])
> 
> ```
> 
> So I think unbounded ranges do serve a useful purpose.

It doesn't feel "unbound" though in the sense that "you keep going and never stop" or "stop when reaching infinity". It's more like "figureOutYourselfWhereToStop". For example if you had this API:

```swift
extension CGRect {
	func iterateArea(
	    horizontalRange: Range<Float>,
	    verticalRange: Range<Float>,
	    stepWidth: CGFloat,
	    stepHeight: CGFloat,
	    execute: (Range<Float>) -> Void
	)
}

```

to iterate a given CGRect with a certain 2D range and use it:

```swift
// iterate right half
rect.iterateArea(
    horizontalRange: (rect.left + rect.width/2)...,
    verticalRange: rect.top...,
    stepWidth: 0.01,
    stepHeight: 0.01
)

```

here you would assume assume that iteration stops at rectangle boundaries rather than at the natural "unbound" value which is infinity. Maybe "unbound" just a bad name.

> [@aggie33](#):
>
> But the fact that a range operator not including the left bound would have to be spelled like this `.>..` probably influenced the decision.

Sounds plausible.

* * *

> [@QuinceyMorris](#):
>
> That doesn't mean that "openness on the left" would somehow be bad. It's just not something that there seems to be a lot of demand for. By contrast, "openness on the right" fits naturally with constructs like `for index in 0 ..< array.count`, being concise and avoiding an off-by-one error.

I can definitely see how we started with that. Then we extended the concept of range to floating numbers - something unrelated to "for in" loop or "indexes" at all. Compared to integer ranges, open v closed variants of which are in all fairness trivially convertible one to another, extending ranges to floats opened us up to something previously unseen: opened vs closed range of floats to allow things like `"Float.random(in: -1...1)"` vs `"Float.random(in: -1..<1)"`. From there is just one small step to "left open" ranges to get the picture complete, the step we haven't done yet.

---

<div class="post-metadata">

**Author:** ![dcowuno](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dcowuno/32/19360_2.png) [@dcowuno](https://forums.swift.org/u/dcowuno)\
**Post date:** [July 28, 2023, 3:37pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/18 "2023-07-28T15:37:35Z")

</div>

At least be helpful and link to the previous literature, please. Assuming everyone is operating in good faith, OP likely searched for this topic and didn't find whatever previous literature you're referring to. I just searched and couldn't find anything along the lines of an authoritative conclusion on the range topic. The only thing close that I found is a comment saying "we can't change range because ABI stability", which all but admits that the status quo sucks but Swift is entrenched so GG. So do you really fault people for continuing to bring up the topic when they encounter a shitty part of the language? Is there no future major release of Swift planned where an ABI break might be allowable to address some more existential concerns that these discussions could be filed under so that progress can be made towards improvement?

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [July 28, 2023, 4:53pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/19 "2023-07-28T16:53:18Z")

</div>

> [@dcowuno](#):
>
> At least be helpful and link to the previous literature, please. Assuming everyone is operating in good faith, OP likely searched for this topic and didn't find whatever previous literature you're referring to.

A large portion of the relevant discussions take place during the pitch and proposal review phases for the relevant proposal(s), and this is very much the case here. Yes, that involves actually reading a quite extensive amount of prior writing, but it's only right that the person who is kicking off an unsolicited "critique" of an existing design study it in detail. That is not—and cannot be—my job.

> [@dcowuno](#):
>
> Is there no future major release of Swift planned where an ABI break might be allowable to address some more existential concerns that these discussions could be filed under so that progress can be made towards improvement?

I don't see any "existential" concern raised here. There is absolutely no plan for breaking ABI, and even if that weren't a concern, let's be clear that we are here to _evolve_ an existing, broadly used language and not remake it. As the name suggests, this process is fundamentally an accretive one.

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [July 28, 2023, 9:25pm UTC](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317/20 "2023-07-28T21:25:13Z")

</div>

Amongst other things:

> [@Unify and expand Range and its likes](https://forums.swift.org/t/unify-and-expand-range-and-its-likes/38427/4):
>
> There are some technical barriers to this change, like source and ABI compatibility, but I’m going to put those aside, because there’s a more important issue: we wouldn’t want this design. We’ve considered similar ones in the past, but we rejected them in favor of the current solution, where decisions about boundary and direction are baked into the type. Let me explain why. 1. The current design enforces the correctness of iteration. For instance, suppose you try to do this: let myRange = ..\<…

[Next page](https://forums.swift.org/t/critic-notes-about-swift-range-types-jungle/66317.md?page=2)
