[swift-evolution] Strings in Swift 4

Ben Cohen ben_cohen at apple.com
Wed Feb 1 12:37:03 CST 2017

I think Dave has already made these points separately but it probably helps to recap more explicitly the behavior we expect from “x…"

Names are just for discussion purposes, exact naming can be left as a separate discussion from functionality:

- Informally, one-sided ranges are a thing. Formally, there are lower-bounded one-sided ranges, which are created with a postfix ... operator. For now, let’s just fully understand them and come back to upper-bounded ranges later.
- Collections will have a subscript that takes a one-sided range and returns a SubSequence. The behavior of that subscript is that the collection "fills in" the “missing” side with it’s upper/lower bound and uses that two-sided range to return a slice.*
- When the Bound type of a lower-bounded range is countable, it will conform to Sequence.** The behavior of that sequence’s iterator is that it starts at the lower bound, and increments indefinitely.
- One-sided ranges should have ~= defined for use with switch statements, where any value above the bound for a lower-bounded range would return true.

* implementation detail: collections would probably have a generic subscript taking a type conforming to RangeExpression, and that protocol would have a helper extension for filling in the missing range given a collection
**one-sided ranges ought in fact to be infinite Collections… a concept that needs a separate thread

With that defined:

> On Feb 1, 2017, at 5:02 AM, Xiaodi Wu via swift-evolution <swift-evolution at swift.org> wrote:
> I entirely agree with you on the desired behavior of `zip(...)`.
> However, if you insist on 0... being notionally an infinite range, then you would have to insist on `for i in 0...` also trapping. Which is not a big deal, IMO, but will surely make the anti-trapping crowd upset.

Certainly, for i in 0… would trap once the iterator needs to increment past Int.max. If you break out of the loop before this happens, it won’t trap. The statement is the moral equivalent of a C-style for(i = 0; /*nothing*/ ; ++i).

If the anti-trapping crowd are upset, they should be upset with Int, not this range type. The range has no opinion on trapping – its iterator just increments its Bounds type when asked to.

> The bigger issue is that either you must have a lenient/clamping subscript for `arr[0...]` or it too must trap, which is not desired.

Based on the definition I give above, arr[0…] means “from 0 up to the endIndex of arr”. This will not trap.

(there is a legitimate quibble here that this will translate into arr[0..<arr.endIndex], but the … kind of implies arr[0…arr.endIndex] which is invalid. But 0..< is ugly so we should ignore this quibble for the sake of aesthetics)

> However, if `arr[0...]` is clamping, then `[1, 2, 3][100...]` would not trap and instead give you `[]`.

It should trap, because 100..<arr.endIndex is not a valid argument for slicing this array.

> If 0... is regarded as an incomplete range, your example of `zip(...)` could still trap as desired. It would trap on the notional attempt to assign someArray.count to IncompleteRange<T>.inferredUpperBound if count exceeds T.max.

It’s unclear to me whether you are trying to formally define .inferredUpperBound as a property you expect to exist, or if you’re using it as informal shorthand. But there is no implied upper bound to a one-sided lower-bounded range, only an actual lower bound. Operations taking lower-bounded ranges as arguments can infer their own upper bound from context, or not, depending on the functionality they need.

As an example of an alternative inferred upper bound: suppose you want to define your own ordering for ranges, for sorting/display purposes. You decide on a lexicographic ordering first by lower then upper bound. You want one-sided ranges to fit into this ordering. So you infer the upper bound to be infinite, for sorting purposes, so: 0…5 < 0… < 1…4 < 1…5 < 1…. This does not mean the upper bound is implied to be infinite, just that the sorting predicate infers it to be, for the purpose of sorting.

> With such semantics for 0..., [1, 2, 3][0...] would behave as expected without the need for leniency, but [1, 2, 3][100...] would trap as I assume you'd expect.

[1,2,3][0…] is valid because the array has an index of 0, but [100…] isn’t because it doesn’t.

It’s worth noting the difference with Python here. In Python, [][2:] is valid because [][2:n] is valid (both return []). In Swift, [][2…] should be invalid (trap) because [][2..<n] is invalid.

> However, it would make no sense to write `for i in 0...`.

I don’t see how. But regardless, the definition of how countable lower-bounded ranges conform to Sequence is just something to be defined in some way that is useful and intuitive to users. 0… being an indefinitely increasing sequence seems like it’s intuitive to me, as does a[n…] being “a slice from n to the end”.

That definition does not need to be unearthed based on some underlying principles. Defining behavior based on building upon axioms is a useful approach sometimes, but if at any point we are finding they get in the way of implementations within the std lib being intuitive or useful, we should stop trying to bend over backwards to stick with this approach.

> On Tue, Jan 31, 2017 at 21:39 Dave Abrahams <dabrahams at apple.com <mailto:dabrahams at apple.com>> wrote:
> on Tue Jan 31 2017, Xiaodi Wu <xiaodi.wu-AT-gmail.com <http://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.
> 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.
> --
> -Dave
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170201/dea4c03d/attachment.html>

More information about the swift-evolution mailing list