Strings in Swift 4

Therefore I'd conclude that `arr[upTo: i]` is the most consistent spelling. It also yields the sensible result that `arr[from: i][upTo: j] == arr[upTo: j][from: i] == arr[i..<j]`.

There's a lot I dislike about `subscript(upTo/through/from:)`:

1. We have not previously been very satisfied with how understandable these labels are—for instance, we fiddled around with them a lot when we were looking at `stride(from:to/through:by:)` in Swift 3, and eventually settled on the originals because we couldn't find anything better. I don't think entrenching them further makes very much sense.

2. The fact that you *can* write `arr[from: i][upTo: j]`, and that this is equivalent to both `arr[upTo: j][from: i]` and `arr[i..<j]`, seems a bit weird. We aren't typically in the habit of providing redundant APIs like this.

3. Neither Stdlib nor the Apple frameworks currently contain *any* labeled subscripts, so this design would be unprecedented in the core language.

4. After a new programmer learns about subscripting with two-sided ranges, removing one of the bounds is a straightforward extension of what they already know. The argument label solution is more ad-hoc.

5. The argument label solution solves the immediate problem, but doesn't give us anything else.

To understand what I mean by #5, consider the implementation. The plan is to introduce a `RangeExpression` protocol:

  protocol RangeExpression {
    associatedtype Bound: Comparable
    func relative<C: Collection(to collection: C) where C.Index == Bound -> Range<Bound>
  }

And then reduce the many manually-generated variants of `subscript(_: Range<Index>)` in `Collection` to just two:

  protocol Collection {
    ...
    subscript(bounds: Range<Index>) -> SubSequence { get }
    ...
  }
  
  extension Collection {
    ...
    subscript<Bounds: RangeExpression>(bounds: Bounds) where Bounds.Bound == Index -> SubSequence {
      return self[bounds.relative(to: self)]
    }
    ...
  }

This design would automatically, source-compatibly, handle several different existing types you can slice with:

* ClosedRange
* CountableRange
* CountableClosedRange

Plus the new types associated with incomplete ranges:

* IncompleteRange
* IncompleteClosedRange

Plus anything else we, or users, might want to add. For instance, I have a prototype built on `RangeExpression` which lets you write things like:

  myString[.startIndex + 1 ..< .endIndex - 1]

This strikes me as a pretty cool thing that some people might want.

Similarly, IncompleteRange and IncompleteClosedRange can most likely be put to other uses. They could easily fill a gap in `switch` statements, which don't have a good way to express open-ended comparisons except with a `where` clause. As some have mentioned, when applied to a `Strideable` type they *could* be treated as infinite sequences, although it's not clear if we really want to do that. And, you know, sometimes you really *do* have a case where one or both bounds of a range may be missing in some cases; incomplete ranges are a built-in, batteries-included way to model that.

To put it simply, slicing with incomplete ranges gives us several valuable tools we can apply to other problems. Labeled subscripts, on the other hand, are just another weird little thing that you have to memorize, and probably won’t.

+1 in general. But I’m still curious how postfix `…` would impact our options for variadic generics and tuple unpacking in the future.

···

On Feb 1, 2017, at 6:58 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 31, 2017, at 2:04 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

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

I think that is perfectly reasonable, but then it seems weird to be able to iterate over it (with no upper bound) independently of a collection). It would surprise me if

for x in arr[arr.startIndex…] { print(x) }

yielded different results than

for i in arr.startIndex… { print(arr[i]) } // CRASH

which it does under this model.

(I think this how it works... semantically, anyway) Since the upper bound isn't specified, it's inferred from the context.

In the first case, the context is as an index into an array, so the upper bound is inferred to be the last valid index.

In the second case, there is no context, so it goes to Int.max. Then, after the "wrong" context has been established, you try to index an array with numbers from the too-large range.

Semantically speaking, they're pretty different operations. Why is it surprising that they have different results?

I must say, I was originally rather fond of `0...` as a spelling, but IMO, Jaden and others have pointed out a real semantic issue.

A range is, to put it simply, the "stuff" between two end points. A "range with no upper bound" _has to be_ one that continues forever. The upper bound _must_ be infinity.

Depends… Swift doesn’t allow partial initializations, and neither the `.endIndex` nor the `.upperBound` properties of a `Range` are optional. From a strictly syntactic PoV, a "Range without an upperBound” can’t exist without getting into undefined behavior territory.

Plus, mathematically speaking, an infinite range would be written "[x, ∞)", with an open upper bracket. If you write “[x, ∞]”, with a closed upper bracket, that’s kind of a meaningless statement. I would argue that if we’re going to represent that “infinite” range, the closest Swift spelling would be “x..<“. That leaves the mathematically undefined notation of “[x, ∞]”, spelled as "x…” in Swift, free to let us have “x…” or “…x” (which by similar reasoning can’t mean "(∞, x]”) return one of these:
enum IncompleteRange<T> {
    case upperValue(T)
    case lowerValue(T)
}
which we could then pass to the subscript function of a collection to create the actual Range like this:
extension Collection {
    subscript(_ ir: IncompleteRange<Index>) -> SubSequence {
        switch ir {
        case .lowerValue(let lower): return self[lower ..< self.endIndex]
        case .upperValue(let upper): return self[self.startIndex ..< upper]
        }
    }
}

I understand that you can do this from a technical perspective. But I'm arguing it's devoid of semantics. That is, it's a spelling to dress up a number.

It’s not any more devoid of semantics than a partially applied function. It is a number or index with added semantics that it provides a lower (or upper) bound on the possible value specified by its type.

If we treat it as such, we shouldn’t allow users to iterate over it directly:

for x in 0… { // <- doesn’t make sense; only partially specified
  print(“hi”)
}

We __could__ introduce 2 types, `IncompleteRange` and `InfiniteRange`, providing an overload that constructs each. It would never be ambiguous because `InfiniteRange ` would be the only `Sequence` and `IncompleteRange` would be the only one of these two that is accepted as a collections subscript.

This *isn’t* that crazy either. There’s precedent for this too. The `..<` operator used to create both ranges and intervals (though it seems those type have started to merge).

¯\_(ツ)_/¯

···

On Jan 31, 2017, at 4:09 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 31, 2017, at 5:35 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Tue, Jan 31, 2017 at 5:28 PM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

On Jan 31, 2017, at 2:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Tue, Jan 31, 2017 at 3:36 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Jan 31, 2017, at 11:32, Jaden Geller via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

What is such an `IncompleteRange<T>` other than a value of type T? It's not an upper bound or lower bound of anything until it's used to index a collection. Why have a new type (IncompleteRange<T>), a new set of operators (prefix and postfix range operators), and these muddied semantics for something that can be written `subscript(upTo upperBound: Index) -> SubSequence { ... }`? _That_ has unmistakable semantics and requires no new syntax.

Arguing that it adds too much complexity relative to the value it provides is reasonable. The value in this use case is mostly syntactic sugar so it’s relatively easy to make the case that it doesn’t cary its weight here.

The value in Ben’s use case is a more composable alternative to `enumerated`. I find this to be a reasonably compelling example of the kind of thing a partial range might enable.

I also tend to find concise notation important for clarity as long as it isn’t obscure or idiosyncratic. With that in mind, I think I lean in favor of `…` so long as we’re confident we won’t regret it if / when we take up variadic generics and / or tuple unpacking.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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

I think it’s fair to say that we get to decide on the semantics of postfix `…`. “a range with no upper bound” is very reasonable, but wouldn’t another reasonable semantics be “all the rest”, meaning that there *is* an upper bound (the greatest possible value).

"All the rest" is by itself insufficient so far as semantics: all the rest _of what_? Supposing that our supplied lower bound is an integer, it must be all the rest of the integers. It cannot be all the rest of whatever, where whatever might be a collection that you try to subset with `0...`. (Recall that collections move indices, but indices know nothing about the collections.) It would be exceeding fuzzy for postfix `...` to mean "all the rest of whatever I want it to mean"--that, almost tautologically, has no semantics at all.

Under the latter semantics, a `for i in 0…` loop would terminate after reaching Int.max. This is probably not what the user intended and would still crash when used in David’s example, but it’s worth considering.

OK, I'm borderline fine with `0... == 0...Int.max`. It at least provides some semantics (i.e., we're saying `...` refers to all the rest of the values representable by the type used for the lower bound) [**]. But Jaden's point still stands, since it would only be consistent if `for i in arr[0...]` then traps after `arr.count` just like `for i in arr[0...Int.max]` would do. Otherwise, we really are fudging the semantics.

If we really want to be honest about the information a value produced using postfix `…` carries, it is a partial range with only the lower bound specified. This allows us to assign meaning to that partial range using additional context:

* When it is possible to increment Bound directly could be interpreted as an (near?) infinite sequence that either terminates or traps when it reaches an unrepresentable value.
* When Bound is an index and the partial range is used as a subscript argument it can be interpreted to mean “to the end of the collection”.

This still leaves us with an out of bounds crash in David’s example that iterates over a partial range. This is an artifact of `Array` using Int as it’s `Index` rather than an opaque type that does not allow users to increment it directly rather than using a collection.

Is the problem in David’s example really that different than the ability to directly index an array with any `Int` we want? It’s not the kind of thing that developers would do frequently. The first time they try it they will get a crash and will learn not to do it again.

I’m not necessarily arguing one way or the other. I’m simply pointing out that “partial range” is a perfectly reasonable semantics to consider.

[**] It is not perfectly consistent semantically because, as was discussed in threads about our numeric protocols, our integer types are supposed to model all integers, not just the ones that happen to be representable. Our model is imperfect because not all integers fit into finite memory, but that's a modeling artifact and not intentional semantics. IIUC, it would be otherwise difficult to give a good accounting of, say, the semantics of addition if arithmetic overflow were an intentional part of the semantics and not an artifact.

I haven’t followed all of the details of the numeric protocol discussions. With this in mind, I agree with your proposed semantics of trapping after `Int.max` as it sounds more consistent with this intent.

···

On Jan 31, 2017, at 5:20 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Jan 31, 2017 at 5:04 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

I’m not sure if you read Ben’s post regarding `enumerated` or not, but he gave the example of `zip(0…, sequence)` as a more general replacement for `enumerated`. IMO, he makes a pretty strong case for this.

- Dave Sweeris

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

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

I think that is perfectly reasonable, but then it seems weird to be able
to iterate over it (with no upper bound) independently of a collection). It
would surprise me if

for x in arr[arr.startIndex…] { print(x) }

yielded different results than

for i in arr.startIndex… { print(arr[i]) } // CRASH

which it does under this model.

(I *think* this how it works... semantically, anyway) Since the upper
bound isn't specified, it's inferred from the context.

In the first case, the context is as an index into an array, so the
upper bound is inferred to be the last valid index.

In the second case, there is no context, so it goes to Int.max. Then,
*after* the "wrong" context has been established, you try to index an
array with numbers from the too-large range.

Semantically speaking, they're pretty different operations. Why is it
surprising that they have different results?

I must say, I was originally rather fond of `0...` as a spelling, but
IMO, Jaden and others have pointed out a real semantic issue.

A range is, to put it simply, the "stuff" between two end points. A
"range with no upper bound" _has to be_ one that continues forever. The
upper bound _must_ be infinity.

Depends… Swift doesn’t allow partial initializations, and neither the
`.endIndex` nor the `.upperBound` properties of a `Range` are optional.
From a strictly syntactic PoV, a "Range without an upperBound” can’t exist
without getting into undefined behavior territory.

Plus, mathematically speaking, an infinite range would be written "[x,
∞)", with an open upper bracket. If you write “[x, ∞]”, with a *closed*
upper bracket, that’s kind of a meaningless statement. I would argue that
if we’re going to represent that “infinite” range, the closest Swift
spelling would be “x..<“. That leaves the mathematically undefined notation
of “[x, ∞]”, spelled as "x…” in Swift, free to let us have “x…” or “…x”
(which by similar reasoning can’t mean "(∞, x]”) return one of these:

enum IncompleteRange<T> {
    case upperValue(T)
    case lowerValue(T)
}

which we could then pass to the subscript function of a collection to
create the actual Range like this:

extension Collection {
    subscript(_ ir: IncompleteRange<Index>) -> SubSequence {
        switch ir {
        case .lowerValue(let lower): return self[lower ..< self.endIndex]
        case .upperValue(let upper): return self[self.startIndex ..<
upper]
        }
    }
}

I understand that you can do this from a technical perspective. But I'm
arguing it's devoid of semantics. That is, it's a spelling to dress up a
number.

It’s not any more devoid of semantics than a partially applied function.

Yes, but this here is not a partially applied type.

Nor does it square with your proposal that you should be able to use `for i
in 0...` to mean something different from `array[0...]`. We don't have
partially applied functions doubling as function calls with default
arguments.

It is a number or index with added semantics that it provides a lower (or
upper) bound on the possible value specified by its type.

What is such an `IncompleteRange<T>` other than a value of type T? It's
not an upper bound or lower bound of anything until it's used to index a
collection. Why have a new type (IncompleteRange<T>), a new set of
operators (prefix and postfix range operators), and these muddied semantics
for something that can be written `subscript(upTo upperBound: Index) ->
SubSequence { ... }`? _That_ has unmistakable semantics and requires no new
syntax.

Arguing that it adds too much complexity relative to the value it provides
is reasonable. The value in this use case is mostly syntactic sugar so
it’s relatively easy to make the case that it doesn’t cary its weight here.

The value in Ben’s use case is a more composable alternative to
`enumerated`. I find this to be a reasonably compelling example of the
kind of thing a partial range might enable.

Ben's use case is not a "partial range." It's a bona fide range with no
upper bound.

···

On Tue, Jan 31, 2017 at 6:09 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On Jan 31, 2017, at 5:35 PM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:
On Tue, Jan 31, 2017 at 5:28 PM, David Sweeris <davesweeris@mac.com> > wrote:

On Jan 31, 2017, at 2:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Jan 31, 2017 at 3:36 PM, David Sweeris via swift-evolution < >> swift-evolution@swift.org> wrote:

On Jan 31, 2017, at 11:32, Jaden Geller via swift-evolution < >>> swift-evolution@swift.org> wrote:

I also tend to find concise notation important for clarity as long as it
isn’t obscure or idiosyncratic. With that in mind, I think I lean in favor
of `…` so long as we’re confident we won’t regret it if / when we take up
variadic generics and / or tuple unpacking.

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

I think that is perfectly reasonable, but then it seems weird to be able
to iterate over it (with no upper bound) independently of a collection). It
would surprise me if

for x in arr[arr.startIndex…] { print(x) }

yielded different results than

for i in arr.startIndex… { print(arr[i]) } // CRASH

which it does under this model.

(I *think* this how it works... semantically, anyway) Since the upper
bound isn't specified, it's inferred from the context.

In the first case, the context is as an index into an array, so the
upper bound is inferred to be the last valid index.

In the second case, there is no context, so it goes to Int.max. Then,
*after* the "wrong" context has been established, you try to index an
array with numbers from the too-large range.

Semantically speaking, they're pretty different operations. Why is it
surprising that they have different results?

I must say, I was originally rather fond of `0...` as a spelling, but
IMO, Jaden and others have pointed out a real semantic issue.

A range is, to put it simply, the "stuff" between two end points. A
"range with no upper bound" _has to be_ one that continues forever. The
upper bound _must_ be infinity.

Depends… Swift doesn’t allow partial initializations, and neither the
`.endIndex` nor the `.upperBound` properties of a `Range` are optional.
From a strictly syntactic PoV, a "Range without an upperBound” can’t exist
without getting into undefined behavior territory.

Plus, mathematically speaking, an infinite range would be written "[x,
∞)", with an open upper bracket. If you write “[x, ∞]”, with a *closed*
upper bracket, that’s kind of a meaningless statement. I would argue that
if we’re going to represent that “infinite” range, the closest Swift
spelling would be “x..<“. That leaves the mathematically undefined notation
of “[x, ∞]”, spelled as "x…” in Swift, free to let us have “x…” or “…x”
(which by similar reasoning can’t mean "(∞, x]”) return one of these:

enum IncompleteRange<T> {
    case upperValue(T)
    case lowerValue(T)
}

which we could then pass to the subscript function of a collection to
create the actual Range like this:

extension Collection {
    subscript(_ ir: IncompleteRange<Index>) -> SubSequence {
        switch ir {
        case .lowerValue(let lower): return self[lower ..< self.endIndex]
        case .upperValue(let upper): return self[self.startIndex ..<
upper]
        }
    }
}

I understand that you can do this from a technical perspective. But I'm
arguing it's devoid of semantics. That is, it's a spelling to dress up a
number.

It’s not any more devoid of semantics than a partially applied function.
It is a number or index with added semantics that it provides a lower (or
upper) bound on the possible value specified by its type.

If we treat it as such, we shouldn’t allow users to iterate over it
directly:

for x in 0… { // <- doesn’t make sense; only partially specified
  print(“hi”)
}

We __could__ introduce 2 types, `IncompleteRange` and `InfiniteRange`,
providing an overload that constructs each. It would never be ambiguous
because `InfiniteRange ` would be the only `Sequence` and `IncompleteRange`
would be the only one of these two that is accepted as a collections
subscript.

This *isn’t* that crazy either. There’s precedent for this too. The `..<`
operator used to create both ranges and intervals (though it seems those
type have started to merge).

¯\_(ツ)_/¯

Mercifully, those types have completely merged AFAIK. IMO, the long-term
aim should be to have ... and ..< produce only one kind of range.

What is such an `IncompleteRange<T>` other than a value of type T? It's not

···

On Tue, Jan 31, 2017 at 6:15 PM, Jaden Geller <jaden.geller@gmail.com> wrote:

On Jan 31, 2017, at 4:09 PM, Matthew Johnson via swift-evolution < > swift-evolution@swift.org> wrote:
On Jan 31, 2017, at 5:35 PM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:
On Tue, Jan 31, 2017 at 5:28 PM, David Sweeris <davesweeris@mac.com> > wrote:

On Jan 31, 2017, at 2:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Jan 31, 2017 at 3:36 PM, David Sweeris via swift-evolution < >> swift-evolution@swift.org> wrote:

On Jan 31, 2017, at 11:32, Jaden Geller via swift-evolution < >>> swift-evolution@swift.org> wrote:

an upper bound or lower bound of anything until it's used to index a
collection. Why have a new type (IncompleteRange<T>), a new set of
operators (prefix and postfix range operators), and these muddied semantics
for something that can be written `subscript(upTo upperBound: Index) ->
SubSequence { ... }`? _That_ has unmistakable semantics and requires no new
syntax.

Arguing that it adds too much complexity relative to the value it provides
is reasonable. The value in this use case is mostly syntactic sugar so
it’s relatively easy to make the case that it doesn’t cary its weight here.

The value in Ben’s use case is a more composable alternative to
`enumerated`. I find this to be a reasonably compelling example of the
kind of thing a partial range might enable.

I also tend to find concise notation important for clarity as long as it
isn’t obscure or idiosyncratic. With that in mind, I think I lean in favor
of `…` so long as we’re confident we won’t regret it if / when we take up
variadic generics and / or tuple unpacking.

_______________________________________________
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

I think that is perfectly reasonable, but then it seems weird to be able to iterate over it (with no upper bound) independently of a collection). It would surprise me if

for x in arr[arr.startIndex…] { print(x) }

yielded different results than

for i in arr.startIndex… { print(arr[i]) } // CRASH

which it does under this model.

(I think this how it works... semantically, anyway) Since the upper bound isn't specified, it's inferred from the context.

In the first case, the context is as an index into an array, so the upper bound is inferred to be the last valid index.

In the second case, there is no context, so it goes to Int.max. Then, after the "wrong" context has been established, you try to index an array with numbers from the too-large range.

Semantically speaking, they're pretty different operations. Why is it surprising that they have different results?

I must say, I was originally rather fond of `0...` as a spelling, but IMO, Jaden and others have pointed out a real semantic issue.

A range is, to put it simply, the "stuff" between two end points. A "range with no upper bound" _has to be_ one that continues forever. The upper bound _must_ be infinity.

Depends… Swift doesn’t allow partial initializations, and neither the `.endIndex` nor the `.upperBound` properties of a `Range` are optional. From a strictly syntactic PoV, a "Range without an upperBound” can’t exist without getting into undefined behavior territory.

Plus, mathematically speaking, an infinite range would be written "[x, ∞)", with an open upper bracket. If you write “[x, ∞]”, with a closed upper bracket, that’s kind of a meaningless statement. I would argue that if we’re going to represent that “infinite” range, the closest Swift spelling would be “x..<“. That leaves the mathematically undefined notation of “[x, ∞]”, spelled as "x…” in Swift, free to let us have “x…” or “…x” (which by similar reasoning can’t mean "(∞, x]”) return one of these:
enum IncompleteRange<T> {
    case upperValue(T)
    case lowerValue(T)
}
which we could then pass to the subscript function of a collection to create the actual Range like this:
extension Collection {
    subscript(_ ir: IncompleteRange<Index>) -> SubSequence {
        switch ir {
        case .lowerValue(let lower): return self[lower ..< self.endIndex]
        case .upperValue(let upper): return self[self.startIndex ..< upper]
        }
    }
}

I understand that you can do this from a technical perspective. But I'm arguing it's devoid of semantics. That is, it's a spelling to dress up a number.

It’s not any more devoid of semantics than a partially applied function. It is a number or index with added semantics that it provides a lower (or upper) bound on the possible value specified by its type.

If we treat it as such, we shouldn’t allow users to iterate over it directly:

for x in 0… { // <- doesn’t make sense; only partially specified
  print(“hi”)
}

We __could__ introduce 2 types, `IncompleteRange` and `InfiniteRange`, providing an overload that constructs each. It would never be ambiguous because `InfiniteRange ` would be the only `Sequence` and `IncompleteRange` would be the only one of these two that is accepted as a collections subscript.

This *isn’t* that crazy either. There’s precedent for this too. The `..<` operator used to create both ranges and intervals (though it seems those type have started to merge).

¯\_(ツ)_/¯

This is what I was getting at, but hadn’t thought the details all the way through to realize that we really would need two distinct types here. I apologize for being a little bit sloppy in both my reasoning and my terminology. Thanks for clearing up the details! :)

···

On Jan 31, 2017, at 6:15 PM, Jaden Geller <jaden.geller@gmail.com> wrote:

On Jan 31, 2017, at 4:09 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 31, 2017, at 5:35 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Tue, Jan 31, 2017 at 5:28 PM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

On Jan 31, 2017, at 2:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Tue, Jan 31, 2017 at 3:36 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Jan 31, 2017, at 11:32, Jaden Geller via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

What is such an `IncompleteRange<T>` other than a value of type T? It's not an upper bound or lower bound of anything until it's used to index a collection. Why have a new type (IncompleteRange<T>), a new set of operators (prefix and postfix range operators), and these muddied semantics for something that can be written `subscript(upTo upperBound: Index) -> SubSequence { ... }`? _That_ has unmistakable semantics and requires no new syntax.

Arguing that it adds too much complexity relative to the value it provides is reasonable. The value in this use case is mostly syntactic sugar so it’s relatively easy to make the case that it doesn’t cary its weight here.

The value in Ben’s use case is a more composable alternative to `enumerated`. I find this to be a reasonably compelling example of the kind of thing a partial range might enable.

I also tend to find concise notation important for clarity as long as it isn’t obscure or idiosyncratic. With that in mind, I think I lean in favor of `…` so long as we’re confident we won’t regret it if / when we take up variadic generics and / or tuple unpacking.

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

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

I am also +1.

I’m still curious how postfix `…` would impact our options for variadic
generics and tuple unpacking in the future.

Somebody who happens to have originally created Swift addressed this point
last week:

In any case, it seems like an obviously good tradeoff to make the syntax
for variadic generics more complicated if it makes one sided ranges more
beautiful.

-Chris

I think we should start a new thread for the discussion of incomplete
ranges though.

Nevin

···

On Wed, Feb 1, 2017 at 9:29 AM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:
On Wed, Jan 25, 2017 at 8:49 PM, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

On Wed, Feb 1, 2017 at 9:29 AM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

> On Feb 1, 2017, at 6:58 AM, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:
>
>> On Jan 31, 2017, at 2:04 PM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> Therefore I'd conclude that `arr[upTo: i]` is the most consistent
spelling. It also yields the sensible result that `arr[from: i][upTo: j] ==
arr[upTo: j][from: i] == arr[i..<j]`.
>
> There's a lot I dislike about `subscript(upTo/through/from:)`:
>
> 1. We have not previously been very satisfied with how understandable
these labels are—for instance, we fiddled around with them a lot when we
were looking at `stride(from:to/through:by:)` in Swift 3, and eventually
settled on the originals because we couldn't find anything better. I don't
think entrenching them further makes very much sense.
>
> 2. The fact that you *can* write `arr[from: i][upTo: j]`, and that this
is equivalent to both `arr[upTo: j][from: i]` and `arr[i..<j]`, seems a bit
weird. We aren't typically in the habit of providing redundant APIs like
this.
>
> 3. Neither Stdlib nor the Apple frameworks currently contain *any*
labeled subscripts, so this design would be unprecedented in the core
language.
>
> 4. After a new programmer learns about subscripting with two-sided
ranges, removing one of the bounds is a straightforward extension of what
they already know. The argument label solution is more ad-hoc.
>
> 5. The argument label solution solves the immediate problem, but doesn't
give us anything else.
>
> To understand what I mean by #5, consider the implementation. The plan
is to introduce a `RangeExpression` protocol:
>
> protocol RangeExpression {
> associatedtype Bound: Comparable
> func relative<C: Collection(to collection: C) where
C.Index == Bound -> Range<Bound>
> }
>
> And then reduce the many manually-generated variants of `subscript(_:
Range<Index>)` in `Collection` to just two:
>
> protocol Collection {
> ...
> subscript(bounds: Range<Index>) -> SubSequence { get }
> ...
> }
>
> extension Collection {
> ...
> subscript<Bounds: RangeExpression>(bounds: Bounds) where
Bounds.Bound == Index -> SubSequence {
> return self[bounds.relative(to: self)]
> }
> ...
> }
>
> This design would automatically, source-compatibly, handle several
different existing types you can slice with:
>
> * ClosedRange
> * CountableRange
> * CountableClosedRange
>
> Plus the new types associated with incomplete ranges:
>
> * IncompleteRange
> * IncompleteClosedRange
>
> Plus anything else we, or users, might want to add. For instance, I have
a prototype built on `RangeExpression` which lets you write things like:
>
> myString[.startIndex + 1 ..< .endIndex - 1]
>
> This strikes me as a pretty cool thing that some people might want.
>
> Similarly, IncompleteRange and IncompleteClosedRange can most likely be
put to other uses. They could easily fill a gap in `switch` statements,
which don't have a good way to express open-ended comparisons except with a
`where` clause. As some have mentioned, when applied to a `Strideable` type
they *could* be treated as infinite sequences, although it's not clear if
we really want to do that. And, you know, sometimes you really *do* have a
case where one or both bounds of a range may be missing in some cases;
incomplete ranges are a built-in, batteries-included way to model that.
>
> To put it simply, slicing with incomplete ranges gives us several
valuable tools we can apply to other problems. Labeled subscripts, on the
other hand, are just another weird little thing that you have to memorize,
and probably won’t.

+1 in general. But I’m still curious how postfix `…` would impact our
options for variadic generics and tuple unpacking in the future.

>
> --
> Brent Royal-Gordon
> Architechies
>
> _______________________________________________
> 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

Brent--

In general, I agree with the direction of your thinking. Time constraints
prevent me from responding as thoroughly as I'd like, but briefly, where we
don't agree at the moment boil down to a few points:

* Agree that "to" vs "through" is not ideal. However, having spent a lot of
time on this issue, I'm quite convinced there is no single word in English
that will accurately describe the distinction. Certainly, we can mimic the
English language by doing something like `stride(from: a, to: b,
.exclusive, by: c)`. However, one other solution to provide clarity is to
pick a convention and solidify user understanding by frequent and
consistent use, which is the diametric opposite of your stance that we
shouldn't use the convention elsewhere.

In general, I think we contort ourselves in the wrong way when the first
listed motivation for a new feature is to work around a naming difficulty:
the solution to not having found the right name is to find the right name.

* It is not redundant API simply because the same thing can be achieved by
composing two other functions. One must assess ergonomics, footgun
likelihood, etc. But in any case, the point you make here is equally
applicable to your proposed feature, i.e. arr[0...][..<2] == arr[0..<2].

* I fully expect subscript labels to start making an appearance in stdlib
APIs sooner or later. I don't think its absence is indicative of a
conscious rejection of them, just that they haven't been needed. (Lenient
subscripts will one day make an appearance, given how often it's requested,
and I sure hope they are labeled.)

* Disagree that the labeled subscript is ad-hoc. In the contrary, I think
"incomplete ranges" are the more ad-hoc choice for array subscripting. My
point is that an "incomplete range" is, by definition, not a range; it is a
bound, which is an Index. We don't need a wrapper around the Index in the
form of IncompleteRange<Index> because it provides little if any semantic
value: the bounds of such a range are not knowable divorced from the
sequence being indexed, and so the upperboundedness or lowerboundedness of
IncompleteRange<Index> is not a useful piece of information without the
sequence. Thus, it is more appropriately a label when you invoke a
subscript on a sequence, not a property of the argument, which is a bound.

* Removing one of the bounds _is_ a natural impulse, but the natural result
one would expect to get is a half-unbounded range (i.e. infinite range),
not an "incomplete range." As I argued above, one must distinguish these
semantics. You are proposing something with the semantics of an "incomplete
range."

* Your example of switch statements is (a) mostly sugar (the exhaustiveness
checking made possible could also be proposed independent of syntax
changes); and (b) arguably an infinite range in semantics. I'll have to
think more on this, though.

Bottom line, I can support an infinite range, but I don't think I like the
idea of an "incomplete range." An "incomplete range" is an Index used as a
lower or upper bound and should be expressed as a plain Index, because it
is not a range. An infinite range, OTOH, is a range. However, the array
subscripting use case is not a use case for infinite ranges, as I've argued
above.

···

On Wed, Feb 1, 2017 at 08:29 Matthew Johnson <matthew@anandabits.com> wrote:

> On Feb 1, 2017, at 6:58 AM, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:
>
>> On Jan 31, 2017, at 2:04 PM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> Therefore I'd conclude that `arr[upTo: i]` is the most consistent
spelling. It also yields the sensible result that `arr[from: i][upTo: j] ==
arr[upTo: j][from: i] == arr[i..<j]`.
>
> There's a lot I dislike about `subscript(upTo/through/from:)`:
>
> 1. We have not previously been very satisfied with how understandable
these labels are—for instance, we fiddled around with them a lot when we
were looking at `stride(from:to/through:by:)` in Swift 3, and eventually
settled on the originals because we couldn't find anything better. I don't
think entrenching them further makes very much sense.
>
> 2. The fact that you *can* write `arr[from: i][upTo: j]`, and that this
is equivalent to both `arr[upTo: j][from: i]` and `arr[i..<j]`, seems a bit
weird. We aren't typically in the habit of providing redundant APIs like
this.
>
> 3. Neither Stdlib nor the Apple frameworks currently contain *any*
labeled subscripts, so this design would be unprecedented in the core
language.
>
> 4. After a new programmer learns about subscripting with two-sided
ranges, removing one of the bounds is a straightforward extension of what
they already know. The argument label solution is more ad-hoc.
>
> 5. The argument label solution solves the immediate problem, but doesn't
give us anything else.
>
> To understand what I mean by #5, consider the implementation. The plan
is to introduce a `RangeExpression` protocol:
>
> protocol RangeExpression {
> associatedtype Bound: Comparable
> func relative<C: Collection(to collection: C) where
C.Index == Bound -> Range<Bound>
> }
>
> And then reduce the many manually-generated variants of `subscript(_:
Range<Index>)` in `Collection` to just two:
>
> protocol Collection {
> ...
> subscript(bounds: Range<Index>) -> SubSequence { get }
> ...
> }
>
> extension Collection {
> ...
> subscript<Bounds: RangeExpression>(bounds: Bounds) where
Bounds.Bound == Index -> SubSequence {
> return self[bounds.relative(to: self)]
> }
> ...
> }
>
> This design would automatically, source-compatibly, handle several
different existing types you can slice with:
>
> * ClosedRange
> * CountableRange
> * CountableClosedRange
>
> Plus the new types associated with incomplete ranges:
>
> * IncompleteRange
> * IncompleteClosedRange
>
> Plus anything else we, or users, might want to add. For instance, I have
a prototype built on `RangeExpression` which lets you write things like:
>
> myString[.startIndex + 1 ..< .endIndex - 1]
>
> This strikes me as a pretty cool thing that some people might want.
>
> Similarly, IncompleteRange and IncompleteClosedRange can most likely be
put to other uses. They could easily fill a gap in `switch` statements,
which don't have a good way to express open-ended comparisons except with a
`where` clause. As some have mentioned, when applied to a `Strideable` type
they *could* be treated as infinite sequences, although it's not clear if
we really want to do that. And, you know, sometimes you really *do* have a
case where one or both bounds of a range may be missing in some cases;
incomplete ranges are a built-in, batteries-included way to model that.
>
> To put it simply, slicing with incomplete ranges gives us several
valuable tools we can apply to other problems. Labeled subscripts, on the
other hand, are just another weird little thing that you have to memorize,
and probably won’t.

+1 in general. But I’m still curious how postfix `…` would impact our
options for variadic generics and tuple unpacking in the future.

>
> --
> Brent Royal-Gordon
> Architechies
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

I think this best summarizes my problem with simultaneously treating `0…` as a partially specified range (waiting for another “argument” from the collection) and as an infinite range (defaulting that argument to Int.max).

···

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

We don't have partially applied functions doubling as function calls with default arguments.

I think that is perfectly reasonable, but then it seems weird to be able to iterate over it (with no upper bound) independently of a collection). It would surprise me if

for x in arr[arr.startIndex…] { print(x) }

yielded different results than

for i in arr.startIndex… { print(arr[i]) } // CRASH

which it does under this model.

(I think this how it works... semantically, anyway) Since the upper bound isn't specified, it's inferred from the context.

In the first case, the context is as an index into an array, so the upper bound is inferred to be the last valid index.

In the second case, there is no context, so it goes to Int.max. Then, after the "wrong" context has been established, you try to index an array with numbers from the too-large range.

Semantically speaking, they're pretty different operations. Why is it surprising that they have different results?

I must say, I was originally rather fond of `0...` as a spelling, but IMO, Jaden and others have pointed out a real semantic issue.

A range is, to put it simply, the "stuff" between two end points. A "range with no upper bound" _has to be_ one that continues forever. The upper bound _must_ be infinity.

Depends… Swift doesn’t allow partial initializations, and neither the `.endIndex` nor the `.upperBound` properties of a `Range` are optional. From a strictly syntactic PoV, a "Range without an upperBound” can’t exist without getting into undefined behavior territory.

Plus, mathematically speaking, an infinite range would be written "[x, ∞)", with an open upper bracket. If you write “[x, ∞]”, with a closed upper bracket, that’s kind of a meaningless statement. I would argue that if we’re going to represent that “infinite” range, the closest Swift spelling would be “x..<“. That leaves the mathematically undefined notation of “[x, ∞]”, spelled as "x…” in Swift, free to let us have “x…” or “…x” (which by similar reasoning can’t mean "(∞, x]”) return one of these:
enum IncompleteRange<T> {
    case upperValue(T)
    case lowerValue(T)
}
which we could then pass to the subscript function of a collection to create the actual Range like this:
extension Collection {
    subscript(_ ir: IncompleteRange<Index>) -> SubSequence {
        switch ir {
        case .lowerValue(let lower): return self[lower ..< self.endIndex]
        case .upperValue(let upper): returnself[self.startIndex ..< upper]
        }
    }
}

I understand that you can do this from a technical perspective. But I'm arguing it's devoid of semantics. That is, it's a spelling to dress up a number.

It’s not any more devoid of semantics than a partially applied function. It is a number or index with added semantics that it provides a lower (or upper) bound on the possible value specified by its type.

If we treat it as such, we shouldn’t allow users to iterate over it directly:

for x in 0… { // <- doesn’t make sense; only partially specified
  print(“hi”)
}

We __could__ introduce 2 types, `IncompleteRange` and `InfiniteRange`, providing an overload that constructs each. It would never be ambiguous because `InfiniteRange ` would be the only `Sequence` and `IncompleteRange` would be the only one of these two that is accepted as a collections subscript.

This *isn’t* that crazy either. There’s precedent for this too. The `..<` operator used to create both ranges and intervals (though it seems those type have started to merge).

¯\_(ツ)_/¯

Mercifully, those types have completely merged AFAIK. IMO, the long-term aim should be to have ... and ..< produce only one kind of range.

There are still 2 variants (`Range` and `CountableRange`), but I imagine conditional conformances will combine those entirely.

(I hope conditional conformances are still in scope for Swift 4. They seem to have a very significant ABI impact.)

···

On Jan 31, 2017, at 4:20 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Jan 31, 2017 at 6:15 PM, Jaden Geller <jaden.geller@gmail.com <mailto:jaden.geller@gmail.com>> wrote:

On Jan 31, 2017, at 4:09 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 31, 2017, at 5:35 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Tue, Jan 31, 2017 at 5:28 PM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

On Jan 31, 2017, at 2:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Tue, Jan 31, 2017 at 3:36 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Jan 31, 2017, at 11:32, Jaden Geller via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

What is such an `IncompleteRange<T>` other than a value of type T? It's not an upper bound or lower bound of anything until it's used to index a collection. Why have a new type (IncompleteRange<T>), a new set of operators (prefix and postfix range operators), and these muddied semantics for something that can be written `subscript(upTo upperBound: Index) -> SubSequence { ... }`? _That_ has unmistakable semantics and requires no new syntax.

Arguing that it adds too much complexity relative to the value it provides is reasonable. The value in this use case is mostly syntactic sugar so it’s relatively easy to make the case that it doesn’t cary its weight here.

The value in Ben’s use case is a more composable alternative to `enumerated`. I find this to be a reasonably compelling example of the kind of thing a partial range might enable.

I also tend to find concise notation important for clarity as long as it isn’t obscure or idiosyncratic. With that in mind, I think I lean in favor of `…` so long as we’re confident we won’t regret it if / when we take up variadic generics and / or tuple unpacking.

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

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

I think it’s fair to say that we get to decide on the semantics of
postfix `…`. “a range with no upper bound” is very reasonable, but
wouldn’t another reasonable semantics be “all the rest”, meaning that there
*is* an upper bound (the greatest possible value).

"All the rest" is by itself insufficient so far as semantics: all the rest
_of what_? Supposing that our supplied lower bound is an integer, it must
be all the rest of the integers. It cannot be all the rest of whatever,
where whatever might be a collection that you try to subset with `0...`.
(Recall that collections move indices, but indices know nothing about the
collections.) It would be exceeding fuzzy for postfix `...` to mean "all
the rest of whatever I want it to mean"--that, almost tautologically, has
no semantics at all.

Under the latter semantics, a `for i in 0…` loop would terminate after

reaching Int.max. This is probably not what the user intended and would
still crash when used in David’s example, but it’s worth considering.

OK, I'm borderline fine with `0... == 0...Int.max`. It at least provides
some semantics (i.e., we're saying `...` refers to all the rest of the
values representable by the type used for the lower bound) [**]. But
Jaden's point still stands, since it would only be consistent if `for i in
arr[0...]` then traps after `arr.count` just like `for i in
arr[0...Int.max]` would do. Otherwise, we really are fudging the semantics.

If we really want to be honest about the information a value produced
using postfix `…` carries, it is a partial range with only the lower bound
specified. This allows us to assign meaning to that partial range using
additional context:

* When it is possible to increment Bound directly could be interpreted as
an (near?) infinite sequence that either terminates or traps when it
reaches an unrepresentable value.
* When Bound is an index and the partial range is used as a subscript
argument it can be interpreted to mean “to the end of the collection”.

These are two very different semantics. One says, `i...` is a range with no
upper bound; the other says, `i...` is a lower bound of something. The
objection is twofold. In the first place, those shouldn't be spelled the
same way. In the second place, the "lower bound of something" is already
adequately accommodated by using, well, the actual lower bound. Well, also,
the "range with no upper bound" isn't very useful in practice, especially
if the only compelling use case is one for replacing an existing API that,
well, there is no consensus yet to replace.

This still leaves us with an out of bounds crash in David’s example that

···

On Tue, Jan 31, 2017 at 5:58 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On Jan 31, 2017, at 5:20 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Jan 31, 2017 at 5:04 PM, Matthew Johnson <matthew@anandabits.com> > wrote:
iterates over a partial range. This is an artifact of `Array` using Int as
it’s `Index` rather than an opaque type that does not allow users to
increment it directly rather than using a collection.

Is the problem in David’s example really that different than the ability
to directly index an array with any `Int` we want? It’s not the kind of
thing that developers would do frequently. The first time they try it they
will get a crash and will learn not to do it again.

I’m not necessarily arguing one way or the other. I’m simply pointing out
that “partial range” is a perfectly reasonable semantics to consider.

[**] It is not perfectly consistent semantically because, as was discussed
in threads about our numeric protocols, our integer types are supposed to
model all integers, not just the ones that happen to be representable. Our
model is imperfect because not all integers fit into finite memory, but
that's a modeling artifact and not intentional semantics. IIUC, it would be
otherwise difficult to give a good accounting of, say, the semantics of
addition if arithmetic overflow were an intentional part of the semantics
and not an artifact.

I haven’t followed all of the details of the numeric protocol
discussions. With this in mind, I agree with your proposed semantics of
trapping after `Int.max` as it sounds more consistent with this intent.

I’m not sure if you read Ben’s post regarding `enumerated` or not, but he

gave the example of `zip(0…, sequence)` as a more general replacement for
`enumerated`. IMO, he makes a pretty strong case for this.

- Dave Sweeris

_______________________________________________
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

I think that is perfectly reasonable, but then it seems weird to be
able to iterate over it (with no upper bound) independently of a
collection). It would surprise me if

for x in arr[arr.startIndex…] { print(x) }

yielded different results than

for i in arr.startIndex… { print(arr[i]) } // CRASH

which it does under this model.

(I *think* this how it works... semantically, anyway) Since the upper
bound isn't specified, it's inferred from the context.

In the first case, the context is as an index into an array, so the
upper bound is inferred to be the last valid index.

In the second case, there is no context, so it goes to Int.max. Then,
*after* the "wrong" context has been established, you try to index an
array with numbers from the too-large range.

Semantically speaking, they're pretty different operations. Why is it
surprising that they have different results?

I must say, I was originally rather fond of `0...` as a spelling, but
IMO, Jaden and others have pointed out a real semantic issue.

A range is, to put it simply, the "stuff" between two end points. A
"range with no upper bound" _has to be_ one that continues forever. The
upper bound _must_ be infinity.

Depends… Swift doesn’t allow partial initializations, and neither the
`.endIndex` nor the `.upperBound` properties of a `Range` are optional.
From a strictly syntactic PoV, a "Range without an upperBound” can’t exist
without getting into undefined behavior territory.

Plus, mathematically speaking, an infinite range would be written "[x,
∞)", with an open upper bracket. If you write “[x, ∞]”, with a *closed* upper
bracket, that’s kind of a meaningless statement. I would argue that if
we’re going to represent that “infinite” range, the closest Swift spelling
would be “x..<“. That leaves the mathematically undefined notation of “[x,
∞]”, spelled as "x…” in Swift, free to let us have “x…” or “…x” (which by
similar reasoning can’t mean "(∞, x]”) return one of these:

enum IncompleteRange<T> {
    case upperValue(T)
    case lowerValue(T)
}

which we could then pass to the subscript function of a collection to
create the actual Range like this:

extension Collection {
    subscript(_ ir: IncompleteRange<Index>) -> SubSequence {
        switch ir {
        case .lowerValue(let lower): return self[lower ..< self.endIndex
]
        case .upperValue(let upper): returnself[self.startIndex ..<
upper]
        }
    }
}

I understand that you can do this from a technical perspective. But I'm
arguing it's devoid of semantics. That is, it's a spelling to dress up a
number.

It’s not any more devoid of semantics than a partially applied function.
It is a number or index with added semantics that it provides a lower (or
upper) bound on the possible value specified by its type.

If we treat it as such, we shouldn’t allow users to iterate over it
directly:

for x in 0… { // <- doesn’t make sense; only partially specified
  print(“hi”)
}

We __could__ introduce 2 types, `IncompleteRange` and `InfiniteRange`,
providing an overload that constructs each. It would never be ambiguous
because `InfiniteRange ` would be the only `Sequence` and `IncompleteRange`
would be the only one of these two that is accepted as a collections
subscript.

This *isn’t* that crazy either. There’s precedent for this too. The `..<`
operator used to create both ranges and intervals (though it seems those
type have started to merge).

¯\_(ツ)_/¯

Mercifully, those types have completely merged AFAIK. IMO, the long-term
aim should be to have ... and ..< produce only one kind of range.

There are still 2 variants (`Range` and `CountableRange`), but I imagine
conditional conformances will combine those entirely.

Per Dave, that's the goal :)

(I hope conditional conformances are still in scope for Swift 4. They seem

···

On Tue, Jan 31, 2017 at 6:27 PM, Jaden Geller <jaden.geller@gmail.com> wrote:

On Jan 31, 2017, at 4:20 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Jan 31, 2017 at 6:15 PM, Jaden Geller <jaden.geller@gmail.com> > wrote:

On Jan 31, 2017, at 4:09 PM, Matthew Johnson via swift-evolution < >> swift-evolution@swift.org> wrote:
On Jan 31, 2017, at 5:35 PM, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:
On Tue, Jan 31, 2017 at 5:28 PM, David Sweeris <davesweeris@mac.com> >> wrote:

On Jan 31, 2017, at 2:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Jan 31, 2017 at 3:36 PM, David Sweeris via swift-evolution < >>> swift-evolution@swift.org> wrote:

On Jan 31, 2017, at 11:32, Jaden Geller via swift-evolution < >>>> swift-evolution@swift.org> wrote:

to have a very significant ABI impact.)

What is such an `IncompleteRange<T>` other than a value of type T? It's

not an upper bound or lower bound of anything until it's used to index a
collection. Why have a new type (IncompleteRange<T>), a new set of
operators (prefix and postfix range operators), and these muddied semantics
for something that can be written `subscript(upTo upperBound: Index) ->
SubSequence { ... }`? _That_ has unmistakable semantics and requires no new
syntax.

Arguing that it adds too much complexity relative to the value it
provides is reasonable. The value in this use case is mostly syntactic
sugar so it’s relatively easy to make the case that it doesn’t cary its
weight here.

The value in Ben’s use case is a more composable alternative to
`enumerated`. I find this to be a reasonably compelling example of the
kind of thing a partial range might enable.

I also tend to find concise notation important for clarity as long as it
isn’t obscure or idiosyncratic. With that in mind, I think I lean in favor
of `…` so long as we’re confident we won’t regret it if / when we take up
variadic generics and / or tuple unpacking.

_______________________________________________
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

I think that is perfectly reasonable, but then it seems weird to be able to iterate over it (with no upper bound) independently of a collection). It would surprise me if

for x in arr[arr.startIndex…] { print(x) }

yielded different results than

for i in arr.startIndex… { print(arr[i]) } // CRASH

which it does under this model.

(I think this how it works... semantically, anyway) Since the upper bound isn't specified, it's inferred from the context.

In the first case, the context is as an index into an array, so the upper bound is inferred to be the last valid index.

In the second case, there is no context, so it goes to Int.max. Then, after the "wrong" context has been established, you try to index an array with numbers from the too-large range.

Semantically speaking, they're pretty different operations. Why is it surprising that they have different results?

I must say, I was originally rather fond of `0...` as a spelling, but IMO, Jaden and others have pointed out a real semantic issue.

A range is, to put it simply, the "stuff" between two end points. A "range with no upper bound" _has to be_ one that continues forever. The upper bound _must_ be infinity.

Depends… Swift doesn’t allow partial initializations, and neither the `.endIndex` nor the `.upperBound` properties of a `Range` are optional. From a strictly syntactic PoV, a "Range without an upperBound” can’t exist without getting into undefined behavior territory.

Plus, mathematically speaking, an infinite range would be written "[x, ∞)", with an open upper bracket. If you write “[x, ∞]”, with a closed upper bracket, that’s kind of a meaningless statement. I would argue that if we’re going to represent that “infinite” range, the closest Swift spelling would be “x..<“. That leaves the mathematically undefined notation of “[x, ∞]”, spelled as "x…” in Swift, free to let us have “x…” or “…x” (which by similar reasoning can’t mean "(∞, x]”) return one of these:
enum IncompleteRange<T> {
    case upperValue(T)
    case lowerValue(T)
}
which we could then pass to the subscript function of a collection to create the actual Range like this:
extension Collection {
    subscript(_ ir: IncompleteRange<Index>) -> SubSequence {
        switch ir {
        case .lowerValue(let lower): return self[lower ..< self.endIndex]
        case .upperValue(let upper): return self[self.startIndex ..< upper]
        }
    }
}

I understand that you can do this from a technical perspective. But I'm arguing it's devoid of semantics. That is, it's a spelling to dress up a number.

It’s not any more devoid of semantics than a partially applied function.

Yes, but this here is not a partially applied type.

Nor does it square with your proposal that you should be able to use `for i in 0...` to mean something different from `array[0...]`. We don't have partially applied functions doubling as function calls with default arguments.

I’m not trying to say it’s *exactly* like a partially applied function.

It is a number or index with added semantics that it provides a lower (or upper) bound on the possible value specified by its type.

What is such an `IncompleteRange<T>` other than a value of type T? It's not an upper bound or lower bound of anything until it's used to index a collection. Why have a new type (IncompleteRange<T>), a new set of operators (prefix and postfix range operators), and these muddied semantics for something that can be written `subscript(upTo upperBound: Index) -> SubSequence { ... }`? _That_ has unmistakable semantics and requires no new syntax.

Arguing that it adds too much complexity relative to the value it provides is reasonable. The value in this use case is mostly syntactic sugar so it’s relatively easy to make the case that it doesn’t cary its weight here.

The value in Ben’s use case is a more composable alternative to `enumerated`. I find this to be a reasonably compelling example of the kind of thing a partial range might enable.

Ben's use case is not a "partial range." It's a bona fide range with no upper bound.

Ok, fair enough. Let’s call it an infinite range then.

We can form an infinite range with an Index even if it’s an opaque type that can’t be incremented or decremented. All we need is a comparable Bound which all Indices meet. We can test whether other indices are contained within that infinite range and can clamp it to a tighter range as well. This clamping is what would need to happen when an infinite range is passed to a collection subscript by providing an upper bound.

The only thing unusual about this is that we don’t usually do a bounds check of any kind when subscripting a collection.

···

On Jan 31, 2017, at 6:15 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Jan 31, 2017 at 6:09 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Jan 31, 2017, at 5:35 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Tue, Jan 31, 2017 at 5:28 PM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

On Jan 31, 2017, at 2:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Tue, Jan 31, 2017 at 3:36 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Jan 31, 2017, at 11:32, Jaden Geller via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I also tend to find concise notation important for clarity as long as it isn’t obscure or idiosyncratic. With that in mind, I think I lean in favor of `…` so long as we’re confident we won’t regret it if / when we take up variadic generics and / or tuple unpacking.

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

I am also +1.

I’m still curious how postfix `…` would impact our options for variadic generics and tuple unpacking in the future.

Somebody who happens to have originally created Swift addressed this point last week:

In any case, it seems like an obviously good tradeoff to make the syntax for variadic generics more complicated if it makes one sided ranges more beautiful.

-Chris

Thanks for reminding me of this. I generally agree with Chris, but have no idea what the more complicated syntax for variadic generics might look like. I guess what I’m looking for is some indication of what ideas (if any) there are about what this might look like. A sketch of possible directions would be sufficient to answer the questions lurking in the back of my mind.

I think we should start a new thread for the discussion of incomplete ranges though.

Yes, I agree. They have been discussed in two different threads now and it really feels like incomplete (and infinite) ranges deserve discussion in their own right. Would it make sense to draft a proposal based on the direction Jaden sketched as a point of focus for the thread (especially to help newcomers get up to speed)?

···

On Feb 1, 2017, at 9:15 AM, Nevin Brackett-Rozinsky <nevin.brackettrozinsky@gmail.com> wrote:
On Wed, Feb 1, 2017 at 9:29 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Wed, Jan 25, 2017 at 8:49 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Nevin

On Wed, Feb 1, 2017 at 9:29 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> On Feb 1, 2017, at 6:58 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
>> On Jan 31, 2017, at 2:04 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>
>> Therefore I'd conclude that `arr[upTo: i]` is the most consistent spelling. It also yields the sensible result that `arr[from: i][upTo: j] == arr[upTo: j][from: i] == arr[i..<j]`.
>
> There's a lot I dislike about `subscript(upTo/through/from:)`:
>
> 1. We have not previously been very satisfied with how understandable these labels are—for instance, we fiddled around with them a lot when we were looking at `stride(from:to/through:by:)` in Swift 3, and eventually settled on the originals because we couldn't find anything better. I don't think entrenching them further makes very much sense.
>
> 2. The fact that you *can* write `arr[from: i][upTo: j]`, and that this is equivalent to both `arr[upTo: j][from: i]` and `arr[i..<j]`, seems a bit weird. We aren't typically in the habit of providing redundant APIs like this.
>
> 3. Neither Stdlib nor the Apple frameworks currently contain *any* labeled subscripts, so this design would be unprecedented in the core language.
>
> 4. After a new programmer learns about subscripting with two-sided ranges, removing one of the bounds is a straightforward extension of what they already know. The argument label solution is more ad-hoc.
>
> 5. The argument label solution solves the immediate problem, but doesn't give us anything else.
>
> To understand what I mean by #5, consider the implementation. The plan is to introduce a `RangeExpression` protocol:
>
> protocol RangeExpression {
> associatedtype Bound: Comparable
> func relative<C: Collection(to collection: C) where C.Index == Bound -> Range<Bound>
> }
>
> And then reduce the many manually-generated variants of `subscript(_: Range<Index>)` in `Collection` to just two:
>
> protocol Collection {
> ...
> subscript(bounds: Range<Index>) -> SubSequence { get }
> ...
> }
>
> extension Collection {
> ...
> subscript<Bounds: RangeExpression>(bounds: Bounds) where Bounds.Bound == Index -> SubSequence {
> return self[bounds.relative(to: self)]
> }
> ...
> }
>
> This design would automatically, source-compatibly, handle several different existing types you can slice with:
>
> * ClosedRange
> * CountableRange
> * CountableClosedRange
>
> Plus the new types associated with incomplete ranges:
>
> * IncompleteRange
> * IncompleteClosedRange
>
> Plus anything else we, or users, might want to add. For instance, I have a prototype built on `RangeExpression` which lets you write things like:
>
> myString[.startIndex + 1 ..< .endIndex - 1]
>
> This strikes me as a pretty cool thing that some people might want.
>
> Similarly, IncompleteRange and IncompleteClosedRange can most likely be put to other uses. They could easily fill a gap in `switch` statements, which don't have a good way to express open-ended comparisons except with a `where` clause. As some have mentioned, when applied to a `Strideable` type they *could* be treated as infinite sequences, although it's not clear if we really want to do that. And, you know, sometimes you really *do* have a case where one or both bounds of a range may be missing in some cases; incomplete ranges are a built-in, batteries-included way to model that.
>
> To put it simply, slicing with incomplete ranges gives us several valuable tools we can apply to other problems. Labeled subscripts, on the other hand, are just another weird little thing that you have to memorize, and probably won’t.

+1 in general. But I’m still curious how postfix `…` would impact our options for variadic generics and tuple unpacking in the future.

>
> --
> Brent Royal-Gordon
> Architechies
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

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

Drafting a proposal sounds like a good idea, to establish all the relevant
information in one place. I don’t recall off the top of my head what
directions Jaden sketched out, but as long as the proposal hits the high
points of the uses and benefits, and summarizes the discussion and
alternatives, it should be fine.

I might suggest using Chris’s terminology of “one-sided range”, because
that is both more precise and it renders moot all the “incomplete” vs
“infinite” hairsplitting.

Nevin

I think that is perfectly reasonable, but then it seems weird to be
able to iterate over it (with no upper bound) independently of a
collection). It would surprise me if

for x in arr[arr.startIndex…] { print(x) }

yielded different results than

for i in arr.startIndex… { print(arr[i]) } // CRASH

which it does under this model.

(I *think* this how it works... semantically, anyway) Since the upper
bound isn't specified, it's inferred from the context.

In the first case, the context is as an index into an array, so the
upper bound is inferred to be the last valid index.

In the second case, there is no context, so it goes to Int.max. Then,
*after* the "wrong" context has been established, you try to index an
array with numbers from the too-large range.

Semantically speaking, they're pretty different operations. Why is it
surprising that they have different results?

I must say, I was originally rather fond of `0...` as a spelling, but
IMO, Jaden and others have pointed out a real semantic issue.

A range is, to put it simply, the "stuff" between two end points. A
"range with no upper bound" _has to be_ one that continues forever. The
upper bound _must_ be infinity.

Depends… Swift doesn’t allow partial initializations, and neither the
`.endIndex` nor the `.upperBound` properties of a `Range` are optional.
From a strictly syntactic PoV, a "Range without an upperBound” can’t exist
without getting into undefined behavior territory.

Plus, mathematically speaking, an infinite range would be written "[x,
∞)", with an open upper bracket. If you write “[x, ∞]”, with a *closed*
upper bracket, that’s kind of a meaningless statement. I would argue that
if we’re going to represent that “infinite” range, the closest Swift
spelling would be “x..<“. That leaves the mathematically undefined notation
of “[x, ∞]”, spelled as "x…” in Swift, free to let us have “x…” or “…x”
(which by similar reasoning can’t mean "(∞, x]”) return one of these:

enum IncompleteRange<T> {
    case upperValue(T)
    case lowerValue(T)
}

which we could then pass to the subscript function of a collection to
create the actual Range like this:

extension Collection {
    subscript(_ ir: IncompleteRange<Index>) -> SubSequence {
        switch ir {
        case .lowerValue(let lower): return self[lower ..< self.endIndex
]
        case .upperValue(let upper): return self[self.startIndex ..<
upper]
        }
    }
}

I understand that you can do this from a technical perspective. But I'm
arguing it's devoid of semantics. That is, it's a spelling to dress up a
number.

It’s not any more devoid of semantics than a partially applied function.

Yes, but this here is not a partially applied type.

Nor does it square with your proposal that you should be able to use `for
i in 0...` to mean something different from `array[0...]`. We don't have
partially applied functions doubling as function calls with default
arguments.

I’m not trying to say it’s *exactly* like a partially applied function.

I'm not saying you're arguing that point. I'm saying that there is a
semantic distinction between (1) a range with two bounds where you've only
specified the one, and (2) a range with one bound. There must be an answer
to the question: what is the nature of the upper bound of `0...`? Either it
exists but is not yet known, or it is known that it does not exist (or, it
is not yet known whether or not it exists). But these are not the same
thing!

It is a number or index with added semantics that it provides a lower (or

upper) bound on the possible value specified by its type.

What is such an `IncompleteRange<T>` other than a value of type T? It's
not an upper bound or lower bound of anything until it's used to index a
collection. Why have a new type (IncompleteRange<T>), a new set of
operators (prefix and postfix range operators), and these muddied semantics
for something that can be written `subscript(upTo upperBound: Index) ->
SubSequence { ... }`? _That_ has unmistakable semantics and requires no new
syntax.

Arguing that it adds too much complexity relative to the value it
provides is reasonable. The value in this use case is mostly syntactic
sugar so it’s relatively easy to make the case that it doesn’t cary its
weight here.

The value in Ben’s use case is a more composable alternative to
`enumerated`. I find this to be a reasonably compelling example of the
kind of thing a partial range might enable.

Ben's use case is not a "partial range." It's a bona fide range with no
upper bound.

Ok, fair enough. Let’s call it an infinite range then.

We can form an infinite range with an Index even if it’s an opaque type
that can’t be incremented or decremented. All we need is a comparable
Bound which all Indices meet. We can test whether other indices are
contained within that infinite range and can clamp it to a tighter range as
well. This clamping is what would need to happen when an infinite range is
passed to a collection subscript by providing an upper bound.

The only thing unusual about this is that we don’t usually do a bounds
check of any kind when subscripting a collection.

Precisely. This would be inconsistent. If lenient subscripts as once
proposed were accepted, however, then perhaps `arr[lenient: 0...]` would
make sense.

But that's not getting to the biggest hitch with your proposal. If
subscript were lenient, then `arr[lenient: 42...]` would also have to give
you a result even if `arr.count == 21`.

This is not at all what Dave Abrahams was proposing, though (unless I
totally misunderstand). He truly doesn't want an infinite range. He wants
to use a terser notation for saying: I want x to be the lower bound of a
range for which I don't yet know (or haven't bothered to find out) the
finite upper bound. It would be plainly clear, if spelled as `arr[from:
42]`, that if `arr.count < 43` then this expression will trap, but if
`arr.count >= 43` then this expression will give you the rest of the
elements.

I also tend to find concise notation important for clarity as long as it

···

On Tue, Jan 31, 2017 at 6:40 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On Jan 31, 2017, at 6:15 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Jan 31, 2017 at 6:09 PM, Matthew Johnson <matthew@anandabits.com> > wrote:

On Jan 31, 2017, at 5:35 PM, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:
On Tue, Jan 31, 2017 at 5:28 PM, David Sweeris <davesweeris@mac.com> >> wrote:

On Jan 31, 2017, at 2:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Jan 31, 2017 at 3:36 PM, David Sweeris via swift-evolution < >>> swift-evolution@swift.org> wrote:

On Jan 31, 2017, at 11:32, Jaden Geller via swift-evolution < >>>> swift-evolution@swift.org> wrote:

isn’t obscure or idiosyncratic. With that in mind, I think I lean in favor
of `…` so long as we’re confident we won’t regret it if / when we take up
variadic generics and / or tuple unpacking.

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

Drafting a proposal sounds like a good idea, to establish all the relevant information in one place. I don’t recall off the top of my head what directions Jaden sketched out, but as long as the proposal hits the high points of the uses and benefits, and summarizes the discussion and alternatives, it should be fine.

I might suggest using Chris’s terminology of “one-sided range”, because that is both more precise and it renders moot all the “incomplete” vs “infinite” hairsplitting.

I recommend reading through the discussion Xiaodi and I had yesterday, which Jaden chimed in on. We really do need to make a distinction between incomplete and infinite ranges if we want to support all of the use cases with clean semantics. This isn’t hair splitting.

···

On Feb 1, 2017, at 9:52 AM, Nevin Brackett-Rozinsky <nevin.brackettrozinsky@gmail.com> wrote:

Nevin

I had read the discussion live as it happened. And just now I went back to
see Jaden’s posts again: there are only a handful, and all are quite brief.
To the extent that they “sketched a direction”, I would say no. We should
instead base the proposal on ideas laid out by Dave Abrahams, Brent
Royal-Gordon, Ben Cohen and several others.

Furthermore, we emphatically do *not* need to make the distinction you
claim between “infinite” and “incomplete” ranges, which *is* needless
hairsplitting.

We (meaning Swift Evolution) can define any semantics we like for any
operator we like. The simple, elegant, intuitive behavior for one-sided
ranges is exactly the “do what I mean” approach as described by many people
including Dave Abrahams.

Want to subscript a collection using a one-sided range? Great! If the fixed
bound is inside the collection then go as far as possible (ie. from start
or to end), and if it is outside then trap for index out of bounds.

Want to zip together integers and sequence elements? Great! If the sequence
eventually ends then stop when it does, and if not then trap when the
integer overflows.

Want to iterate over a one-sided range? Well if the upper end is open then
great! “for i in 20...” will loop until it hits a “break” or trap on
overflow. We could probably even make “for i in (...20).reversed” work and
count down, though we don’t have to.

In any case, the point remains: if we do add one-sided ranges, we can
define any behavior we want for them. And given the opinionated nature of
Swift, it follows that we should choose to make them expressive, useful,
and enjoyable.

Nevin

···

On Wed, Feb 1, 2017 at 10:58 AM, Matthew Johnson <matthew@anandabits.com> wrote:

> On Feb 1, 2017, at 9:52 AM, Nevin Brackett-Rozinsky < > nevin.brackettrozinsky@gmail.com> wrote:
>
> Drafting a proposal sounds like a good idea, to establish all the
relevant information in one place. I don’t recall off the top of my head
what directions Jaden sketched out, but as long as the proposal hits the
high points of the uses and benefits, and summarizes the discussion and
alternatives, it should be fine.
>
> I might suggest using Chris’s terminology of “one-sided range”, because
that is both more precise and it renders moot all the “incomplete” vs
“infinite” hairsplitting.

I recommend reading through the discussion Xiaodi and I had yesterday,
which Jaden chimed in on. We really do need to make a distinction between
incomplete and infinite ranges if we want to support all of the use cases
with clean semantics. This isn’t hair splitting.

>
> Nevin

I think that is perfectly reasonable, but then it seems weird to be able to iterate over it (with no upper bound) independently of a collection). It would surprise me if

for x in arr[arr.startIndex…] { print(x) }

yielded different results than

for i in arr.startIndex… { print(arr[i]) } // CRASH

which it does under this model.

(I think this how it works... semantically, anyway) Since the upper bound isn't specified, it's inferred from the context.

In the first case, the context is as an index into an array, so the upper bound is inferred to be the last valid index.

In the second case, there is no context, so it goes to Int.max. Then, after the "wrong" context has been established, you try to index an array with numbers from the too-large range.

Semantically speaking, they're pretty different operations. Why is it surprising that they have different results?

I must say, I was originally rather fond of `0...` as a spelling, but IMO, Jaden and others have pointed out a real semantic issue.

A range is, to put it simply, the "stuff" between two end points. A "range with no upper bound" _has to be_ one that continues forever. The upper bound _must_ be infinity.

Depends… Swift doesn’t allow partial initializations, and neither the `.endIndex` nor the `.upperBound` properties of a `Range` are optional. From a strictly syntactic PoV, a "Range without an upperBound” can’t exist without getting into undefined behavior territory.

Plus, mathematically speaking, an infinite range would be written "[x, ∞)", with an open upper bracket. If you write “[x, ∞]”, with a closed upper bracket, that’s kind of a meaningless statement. I would argue that if we’re going to represent that “infinite” range, the closest Swift spelling would be “x..<“. That leaves the mathematically undefined notation of “[x, ∞]”, spelled as "x…” in Swift, free to let us have “x…” or “…x” (which by similar reasoning can’t mean "(∞, x]”) return one of these:
enum IncompleteRange<T> {
    case upperValue(T)
    case lowerValue(T)
}
which we could then pass to the subscript function of a collection to create the actual Range like this:
extension Collection {
    subscript(_ ir: IncompleteRange<Index>) -> SubSequence {
        switch ir {
        case .lowerValue(let lower): return self[lower ..< self.endIndex]
        case .upperValue(let upper): return self[self.startIndex ..< upper]
        }
    }
}

I understand that you can do this from a technical perspective. But I'm arguing it's devoid of semantics. That is, it's a spelling to dress up a number.

It’s not any more devoid of semantics than a partially applied function.

Yes, but this here is not a partially applied type.

Nor does it square with your proposal that you should be able to use `for i in 0...` to mean something different from `array[0...]`. We don't have partially applied functions doubling as function calls with default arguments.

I’m not trying to say it’s *exactly* like a partially applied function.

I'm not saying you're arguing that point. I'm saying that there is a semantic distinction between (1) a range with two bounds where you've only specified the one, and (2) a range with one bound. There must be an answer to the question: what is the nature of the upper bound of `0...`? Either it exists but is not yet known, or it is known that it does not exist (or, it is not yet known whether or not it exists). But these are not the same thing!

It is a number or index with added semantics that it provides a lower (or upper) bound on the possible value specified by its type.

What is such an `IncompleteRange<T>` other than a value of type T? It's not an upper bound or lower bound of anything until it's used to index a collection. Why have a new type (IncompleteRange<T>), a new set of operators (prefix and postfix range operators), and these muddied semantics for something that can be written `subscript(upTo upperBound: Index) -> SubSequence { ... }`? _That_ has unmistakable semantics and requires no new syntax.

Arguing that it adds too much complexity relative to the value it provides is reasonable. The value in this use case is mostly syntactic sugar so it’s relatively easy to make the case that it doesn’t cary its weight here.

The value in Ben’s use case is a more composable alternative to `enumerated`. I find this to be a reasonably compelling example of the kind of thing a partial range might enable.

Ben's use case is not a "partial range." It's a bona fide range with no upper bound.

Ok, fair enough. Let’s call it an infinite range then.

We can form an infinite range with an Index even if it’s an opaque type that can’t be incremented or decremented. All we need is a comparable Bound which all Indices meet. We can test whether other indices are contained within that infinite range and can clamp it to a tighter range as well. This clamping is what would need to happen when an infinite range is passed to a collection subscript by providing an upper bound.

The only thing unusual about this is that we don’t usually do a bounds check of any kind when subscripting a collection.

Precisely. This would be inconsistent. If lenient subscripts as once proposed were accepted, however, then perhaps `arr[lenient: 0...]` would make sense.

But that's not getting to the biggest hitch with your proposal. If subscript were lenient, then `arr[lenient: 42...]` would also have to give you a result even if `arr.count == 21`.

This is not at all what Dave Abrahams was proposing, though (unless I totally misunderstand). He truly doesn't want an infinite range. He wants to use a terser notation for saying: I want x to be the lower bound of a range for which I don't yet know (or haven't bothered to find out) the finite upper bound. It would be plainly clear, if spelled as `arr[from: 42]`, that if `arr.count < 43` then this expression will trap, but if `arr.count >= 43` then this expression will give you the rest of the elements.

Right. I was not making the necessary distinction between incomplete ranges and infinite ranges. Jaden provided an accurate description of what I was trying to get at and it *does* require both `IncompleteRange` and `InfiniteRange` to do it properly.

I’m not necessarily trying to argue that we *should* do this, only that there isn’t a fundamental semantic problem with it. In a language like Swift there is no fundamental reason that `0…` must semantics independent of context. Allowing context to provide the semantics doesn’t seem any more magical than allowing context to define the type of literals like `0`.

···

On Jan 31, 2017, at 6:54 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Jan 31, 2017 at 6:40 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Jan 31, 2017, at 6:15 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Tue, Jan 31, 2017 at 6:09 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Jan 31, 2017, at 5:35 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Tue, Jan 31, 2017 at 5:28 PM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

On Jan 31, 2017, at 2:04 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Tue, Jan 31, 2017 at 3:36 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Jan 31, 2017, at 11:32, Jaden Geller via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I also tend to find concise notation important for clarity as long as it isn’t obscure or idiosyncratic. With that in mind, I think I lean in favor of `…` so long as we’re confident we won’t regret it if / when we take up variadic generics and / or tuple unpacking.

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

I think you do misunderstand. Notionally, 0... is an infinite range.
The basic programming model for numbers in swift is (to a first
approximation), program as if there's no overflow, and we'll catch you
by trapping if your assumption is wrong. It doesn't make sense for the
semantics of 0... to depend on the deduced type of 0 or the
representable range of Int

for example,

    for x in zip(n..., someArray) {

    }

How many iterations should this give you? If it doesn't process all of
someArray, I want a trap.

···

on Tue Jan 31 2017, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote:

But that's not getting to the biggest hitch with your proposal. If
subscript were lenient, then `arr[lenient: 42...]` would also have to give
you a result even if `arr.count == 21`.

This is not at all what Dave Abrahams was proposing, though (unless I
totally misunderstand). He truly doesn't want an infinite range. He wants
to use a terser notation for saying: I want x to be the lower bound of a
range for which I don't yet know (or haven't bothered to find out) the
finite upper bound. It would be plainly clear, if spelled as `arr[from:
42]`, that if `arr.count < 43` then this expression will trap, but if
`arr.count >= 43` then this expression will give you the rest of the
elements.

--
-Dave