[SHORT Review] SE-0132: Rationalizing Sequence end-operation names


(Chris Lattner) #1

Hello Swift community,

The review of "SE-0132: Rationalizing Sequence end-operation names" begins now and runs through July 26. Apologies for the short review cycle, but we’re right up against the end of source breaking changes for Swift 3. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and contribute to the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * What is your evaluation of the proposal?
  * Is the problem being addressed significant enough to warrant a change to Swift?
  * Does this proposal fit well with the feel and direction of Swift?
  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager


(Jacob Bandes-Storch) #2

https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md

        * What is your evaluation of the proposal?

+1 on drop{First,Last}() → removing{First,Last}().
+1 on drop{First,Last}(_:slight_smile: → removing{Prefix,Suffix}(_:).
+1 on drop(while:) → removingPrefix(while:).
+1 on starts(with:[by:]) → hasPrefix(_:[by:]).
+1 on index({of:,where:}) → firstIndex({of:,where:}).
+1 on prefix and postfix versions of ..< and the prefix version of the ...
operator. For a while I thought I might prefer labeled subscripts, but
after writing them out I agree the operators are better.

The rest is a stream of consciousness:.

I don't think postfix ... is necessary. When does one ever combine a
ClosedRange with endIndex?

I don't agree that "drop" sounds particularly scary, but I'm fine with
"removing" as an alternative to "drop"/"dropping". Scatologists will be
disappointed, I'm sure.

I'd forgotten about prefix(while:), which apparently isn't implemented yet.
I think I'd prefer if this were named prefix(where:) instead.

I'm struggling a little with the distinction between length-based and
index-based methods/subscripts. Are "prefix(_ length: Int)" and
"prefix(upTo end: Index)" really different enough that one of them should
be a subscript and the other a method? The same question applies to
prefix(through:) and suffix(from:). I kinda wish these could all be methods
or all subscripts.

I have to say I don't fully understand the need for (or benefits of)
RangeExpression and the relative(to:) API (perhaps because I don't have
much experience using the most recent collection/index/range APIs). Since
the conforming type's Bound is already required to be the collection's
Index, it seems pointless to have an API to access this as a Range...there
should just be Collection subscript methods which accept the conforming
types. I suppose it's nice to get all these for free by implementing just
one subscript function (excepting the current lack of generic
subscripts)...but is it even possible to express every RangeExpression as a
Range? What about a ClosedRange<Int> whose upperBound is Int.max? (Wasn't
that the whole point of introducing ClosedRange in the first place?)

Random question: why do we have both removeFirst and popFirst? ...on
further reading I see you covered this in Future Directions. I'd be happy
to discuss merging these. I wonder if it's important to do for Swift 3
(although we're basically out of time)?

The name IncompleteRange makes sense, but could be a little misleading if
..<(Bound?,Bound?) is called with two non-nil operands: based on my
reading, it sounds like the returned IncompleteRange wouldn't actually
change when you call completed(by:) (since it's not actually incomplete).
However, I can't come up with any better naming suggestions.

        * Is the problem being addressed significant enough to warrant a
change to Swift?

Yes.

        * Does this proposal fit well with the feel and direction of Swift?

Mostly. Not sure there's enough consistency between
subsequence-length-based and index-based APIs, namely that prefix(upTo:)
becomes a subscript but prefix(_:slight_smile: doesn't.

        * If you have used other languages or libraries with a similar

feature, how do you feel that this proposal compares to those?

I've used Python and Mathematica, which both have functionality similar to
"incomplete range subscripts":

    Python: http://stackoverflow.com/a/509295/23649
    Mathematica: https://reference.wolfram.com/language/ref/Span.html

Based on my experience there, this is a good first (second?) step for Swift
to take.

Both Mathematica and Python support a *stride* as well as start/end indices
in their subscripting syntax. It would be nice for Swift to support
something like this, but some versions of that would require custom ternary
operators (or weird intermediate types to fake a custom ternary operator).
We might also consider labelled multi-argument subscripts like
"collection[start..<end, by: 2]".

They also both support *negative indices *which count from the end. Swift's
suffix-based APIs are limited; I'd like to see an in-depth consideration of
this down the line. Now is probably not the time, unless anyone can think
of reasons it would affect the syntax/naming we choose in this proposal.

(It's also interesting to note that Mathematica's subscripts (Part
<https://reference.wolfram.com/language/ref/Part.html>) support
multi-dimensional arrays: matrix[[3;;4, -5;;]] would be the submatrix
containing the last 5 entries in rows 3 and 4 of the original matrix.)

        * How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?

A mostly-thorough reading of the proposal.


(Xiaodi Wu) #3

Hello Swift community,

The review of "SE-0132: Rationalizing Sequence end-operation names" begins
now and runs through July 26. Apologies for the short review cycle, but
we’re right up against the end of source breaking changes for Swift 3. The
proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md

Reviews are an important part of the Swift evolution process. All reviews
should be sent to the swift-evolution mailing list at

        https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and contribute to the direction of Swift.
When writing your review, here are some questions you might want to answer
in your review:

        * What is your evaluation of the proposal?

The end product is really quite beautiful. This proposal very much succeeds
at rationalizing all names without introducing terminology that's
unprecedented. I very much appreciate that properties or functions named
`first` now always return one or zero elements and that ranges are always
indicated with range operators. The only quibble is that `hasPrefix(_:by:)`
might be more appropriately `hasPrefix(_:comparingBy:)`, as the standalone
preposition doesn't quite make sense IMO.

        * Is the problem being addressed significant enough to warrant a
change to Swift?

Yes.

        * Does this proposal fit well with the feel and direction of Swift?

Yes, very much so.

        * If you have used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?

This proposal rationalizes Swift-specific names, so not sure how to compare
to other languages.

        * How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?

A quick reading of this version; I followed the original discussion and
thought about it more in-depth then.

···

On Mon, Jul 25, 2016 at 1:10 AM, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

More information about the Swift evolution process is available at

        https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chéyo Jiménez) #4

Hello Swift community,

The review of "SE-0132: Rationalizing Sequence end-operation names" begins now and runs through July 26. Apologies for the short review cycle, but we’re right up against the end of source breaking changes for Swift 3. The proposal is available here:

   https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

   https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and contribute to the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

   * What is your evaluation of the proposal?

+1

In the future section :

I strongly dislike every(where:) but I would not be opposed to select(where:) but I think filter, map, reduce etc are way better names.

   * Is the problem being addressed significant enough to warrant a change to Swift?

Yes. There was too much inconsistency.

   * Does this proposal fit well with the feel and direction of Swift?

Yes

   * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
   * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick study.

···

On Jul 24, 2016, at 11:10 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

More information about the Swift evolution process is available at

   https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #5

  * What is your evaluation of the proposal?

I was not totally happy with early drafts of this proposal. The final draft is a significant improvement. I am mostly +1, with a couple of minor critiques.

I think this proposal pushes a bit too hard on consistency of “first / last” vs “prefix / suffix”. Specifically, I think first(n) and last(n) are significantly more clear than prefix(n) and suffix(n) (and removingFirst(n) / removingLast(n) instead of removingPrefix(n) / removingSuffix(n). I think the former are immediately clear. The latter, while consistent in terms of matching semantics with a naming convention suffer in terms of clarity at the call site. I do not think it is immediately clear that the parameter specifies the *length* of the prefix or suffix. removingPrefix(ofLength: n) would solve the clarity issue at the expense of verbosity. I would prefer we just keep first / last in these cases.

Another comment is that you have generally moved index based methods to subscripts rather than named methods. Why didn’t you take this all the way and change `replaceSubrange` to be a subscript setter?

  * Is the problem being addressed significant enough to warrant a change to Swift?

Yes, the new names have more consistency.

  * Does this proposal fit well with the feel and direction of Swift?

Yes.

  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I don’t believed I have used any languages that emphasize consistent naming and API guidelines as strongly as Swift. This is a good direction.

  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A relatively quick read.

···

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charlie Monroe) #6

Hello Swift community,

The review of "SE-0132: Rationalizing Sequence end-operation names" begins now and runs through July 26. Apologies for the short review cycle, but we’re right up against the end of source breaking changes for Swift 3. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and contribute to the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * What is your evaluation of the proposal?

The updated API is more consistent and seems the right step to take.

  * Is the problem being addressed significant enough to warrant a change to Swift?

Yes.

  * Does this proposal fit well with the feel and direction of Swift?

Yes.

  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Read the proposal and skimmed through the discussion.

···

On Jul 25, 2016, at 8:10 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Nate Cook) #7

  https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md

First, a big thanks to Brent for all the work in categorizing, describing, and justifying all these changes! I'm largely in favor of the new method names—several of the renamings lead to better grouping of related methods, and I think the compromises that have been made make lots of sense.

However, I'm not in favor of replacing the existing slicing methods with new subscript syntax that uses partial ranges. These represent a significant new vernacular for Swift that none of us has used before in the language. I'm additionally worried about the discoverability and clarity of this kind of slicing. In the other languages I've used that support similar kinds of subscripting, it always seems too clever by half—fussy to write and frequently confusing to read.

At this late date, I propose keeping the slicing methods (i.e., prefix(upTo:), prefix(through:), and suffix(from:)) and considering adopting the new slicing-via-subscript syntax in a future proposal.

Best,
Nate


(Dave Abrahams) #8

Hello Swift community,

The review of "SE-0132: Rationalizing Sequence end-operation names"
begins now and runs through July 26. Apologies for the short review
cycle, but we’re right up against the end of source breaking changes
for Swift 3. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md

Reviews are an important part of the Swift evolution process. All
reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and contribute to the direction of
Swift. When writing your review, here are some questions you might
want to answer in your review:

  * What is your evaluation of the proposal?

I'm mostly very much in favor of this proposal, but I have some
thoughts.

First, though, I have to apologize for those wide tables, since I'm
listed as a co-author (because of a small design contribution). The
only way I've been able to read them is by looking at the markdown
source, so that's how I'm going to quote it here.

[Note to future authors: if you need to include a table, this is how you
can make it narrow enough:
https://github.com/apple/swift-evolution/blob/master/proposals/0118-closure-parameter-names-and-labels.md#proposed-solution.
The source is awful to read but it renders OK.]

## Proposed solution

We sever the index-taking APIs from the others, forming two separate
families, which we will call the "Sequence-end operations" and the
"index-based operations". We then consider and redesign them along
separate lines.

### Sequence-end operations

Each of these APIs should be renamed to use a directional word based on
its row in the table:

> Operand | Directional word |
> -------------------------------- | ------------------ |
> **Fixed Size** |
> First 1 | first |
> Last 1 | last |
> First (n: Int) | prefix |
> ...with closure | prefix |
> Last (n: Int) | suffix |
> ...with closure | suffix |
> **Searching From End** |
> First matching element | first |
> ...with closure | first |
> Last matching element | last |
> ...with closure | last |

To accomplish this, `starts(with:)` should be renamed to
`hasPrefix(_:)`, and other APIs should have directional words replaced
or added as appropriate.

Additionally, the word `drop` in the "Exclude" APIs should be replaced
with `removing`. These operations omit the same elements which the
`remove` operations delete, so even though the types are not always
the same (`removing` returns `SubSequence`, not `Self`), we think they
are similar enough to deserve to be treated as nonmutating forms.

Unfortunately there's a semantic difference that I hadn't noticed
before: the mutating “remove” operations have a precondition that there
be at least as many elements as are being removed. “Drop,” like “pop,”
is forgiving of such overruns. I think this is solvable; my suggestion
is below

These changes yield (altered names **bold**):

> > Get | Index | Exclude | Remove (1) | Pop (1) | Equate (2) |
> -------------------------------- | ----------------------------- | ------------------------------------- | ----------------------------------------- | --------------------- | ------------ | ------------------------------------------ |
> **Fixed Size** |
> First 1 | C.first | - | **S.removingFirst()** | C.removeFirst() | C.popFirst() | - |
> Last 1 | C.last | - | **S.removingLast()** | C.removeLast() | C.popLast() | - |
> First (n: Int) | S.prefix(3) | - | **S.removingPrefix(3)** | **C.removePrefix(3)** | - | **S.hasPrefix([x,y,z])** |
> ...with closure | S.prefix(while: isPrime) | - | **S.removingPrefix(while: isPrime)** | - | - | **S.hasPrefix([x,y,z], by: ==)** |
> Last (n: Int) | S.suffix(3) | - | **S.removingSuffix(3)** | **C.removeSuffix(3)** | - | - |
> ...with closure | - | - | - | - | - | - |
> **Searching From End** |
> First matching element | - | **C.firstIndex(of: x)** | - | - | - | - |
> ...with closure | S.first(where: isPrime) | **C.firstIndex(where: isPrime)** | - | - | - | - |
> Last matching element | - | - | - | - | - | - |
> ...with closure | - | - | - | - | - | - |

My suggestion would be to make the remove()
operations more forgiving:

   rename popFirst() to removeFirst()
   rename popLast() to removeLast()

   kill removeFirst(n)
   kill removeLast(n)

The “forgiving” forms of x.removeFirst(n) and x.removeLast(n) can be
expressed as:

   let i = x.index(x.startIndex, offsetBy: n, limitedBy: x.endIndex)
   x.removeSubrange(..<i)

   let i = x.index(x.endIndexIndex, offsetBy: -n, limitedBy: x.startIndex)
   x.removeSubrange(i..<)

I realize that's quite verbose. We could of course just make
removePrefix(n) and removeSuffix(n) forgiving, but I have long believed
that the “prefix/suffix” methods should go one of two ways:

a. they get a label that clarifies the meaning of the argument, e.g.

   x.removePrefix(ofMaxLength: n)
   x.removeSuffix(ofMaxLength: n)

b. they are given a recognizable domain-specific notation such as:

   x.removeSubrange($+n..<)
   x.removeSubrange(..<$-n)

   I am strongly in favor of this answer (which is implementable within
   the framework of this proposal) because of the way it reduces API
   surface area and leverages the user's understanding of how ranges
   work.

   It also implies we can replace

     x.removingPrefix(n)
     x.removingSuffix(n)

   with

     x[$+n..<]
     x[..<$-n]

  for Collections.

  That would admittedly leave single-pass Sequences without an API for
  dropping the first N elements. I am inclined to think that interface
  should be moved to Iterator.

  The introduction of such notation raises the question of whether we
  need unary range operators, and could instead go with

     x[i..<$] and x[$..<i]

  which is after all only one character longer than

     x[i..<] and x[..<i]

  and stays out of the territory of the prefix/suffix “pack/unpack”
  operators that are likely to be used for generic variadics.

### Index-based operations

Because these APIs look up elements based on their indices, we believe
these operations should be exposed as subscripts, and ideally should
look like other slicing operations:

let head = people[..<i]
let tail = people[i..<]
let rearrangedPeople = tail + head

<!-- Comment to make my editor happy -->

We will accomplish this by introducing two new types, `IncompleteRange`
and `IncompleteClosedRange`. These are similar to `Range` and
`ClosedRange`, except that the bounds are optional.

To construct them, we will introduce both prefix and suffix operators
taking a non-optional bound, and infix operators taking optional bounds.
(We offer both because `c[..<i]` is more convenient than `c[nil ..< i]`,
but doesn't allow you to dynamically choose between supplying and
omitting a bound.) These will follow the existing convention: `..<` will
construct the half-open `IncompleteRange`, while `...` will construct
`IncompleteClosedRange`.

I believe the `$+n..<i` idea is still implementable with these basic
types, just with an enum instead of optionals. I'll take a shot at it
tonight if I can get a few minutes.

Rather than continuing to proliferate overloads of slicing subscripts,
we will also introduce a new `RangeExpression` protocol which allows
any range-like type to convert itself into a plain `Range<Index>`
appropriate to the collection in question. Thus, there should only be
two range subscripts: one taking `Range<Index>`, and one taking
everything else.

We will also modify the existing `removeSubrange(_:)` and
`replaceSubrange(_:with:)` calls to take `RangeExpression` instances,
thereby merging many existing variants into one while simultaneously
extending them to support `IncompleteRange` and `IncompleteClosedRange`.
Though technically additive, we believe this is an easy win.

Thus, the table above becomes:

> > Type | Get | Remove | Replace |
> ------------------------------------------------ | --------------------------------- | -------------------- | ----------------------------------- | ----------------------------------------------------------- |
> **Based on Index, Arbitrary** |
> (i: Index) ..< (j: Index) | Range\<Index> | C[i ..< j] | C.removeSubrange(i ..< j) | C.replaceSubrange(i ..< j, with: [x,y]) |
> ...Countable | CountableRange\<Index> | C[i ..< j] | C.removeSubrange(i ..< j) | C.replaceSubrange(i ..< j, with: [x,y]) |
> (i: Index) ... (j: Index) | ClosedRange\<Index> | C[i ... j] | C.removeSubrange(i ... j) | C.replaceSubrange(i ... j, with: [x,y]) |
> ...Countable | CountableClosedRange\<Index> | C[i ... j] | C.removeSubrange(i ... j) | C.replaceSubrange(i ... j, with: [x,y]) |
> **Based on Index, From End** |
> startIndex ..< (i: Index) | **IncompleteRange\<Index>** | **C[..\<i]** | **C.removeSubrange(..\<i)** | **C.replaceSubrange(..\<i, with: [x,y])** |
> (i: Index) ..< endIndex | **IncompleteRange\<Index>** | **C[i..\<]** | **C.removeSubrange(i..\<)** | **C.replaceSubrange(i..\<, with: [x,y])** |
> startIndex ... (i: Index) | **IncompleteClosedRange\<Index>** | **C[...i]** | **C.removeSubrange(...i)** | **C.replaceSubrange(...i, with: [x,y])** |

However, it should be implemented with merely:

> > Type | Get | Remove | Replace |
> --------------------------------------------- | --------------------------------------------------- | -------------------- | ----------------------------------- | ----------------------------------------------------------- |
> (i: Index) ..< (j: Index) | Range\<Index> | C[i ..< j] | C.removeSubrange(i ..< j) | C.replaceSubrange(i ..< j, with: [x,y]) |
> Everything else | RangeExpression where Bound == Index | C[i ... j] | C.removeSubrange(i ... j) | C.replaceSubrange(i ... j, with: [x,y]) |

## Detailed design

### Sequence-end operations

The following methods should be renamed as follows wherever they appear
in the standard library. These are simple textual substitutions; we
propose no changes whatsoever to types, parameter interpretations, or
other semantics.

> Old method | New method |
> ------------------------------------------------- | ------------------------------------------------------- |
> `dropFirst() -> SubSequence` | `removingFirst() -> SubSequence` |
> `dropLast() -> SubSequence` | `removingLast() -> SubSequence` |
> `dropFirst(_ n: Int) -> SubSequence` | `removingPrefix(_ n: Int) -> SubSequence` |
> `drop(@noescape while predicate: (Iterator.Element) throws -> Bool) rethrows -> SubSequence` | `removingPrefix(@noescape while predicate: (Iterator.Element) throws -> Bool) rethrows -> SubSequence` |

I'm concerned with how the above fits into the scheme. Writing it out
is:

    x[(x.firstIndex(where: {!predicate($0)}) ?? x.endIndex)..<$]

and that's just for Collections. Drat; we might not be able to get rid
of these “removingPrefix” operations altogether. OK, I'm out of time so
I'll have to get back to this.

···

on Sun Jul 24 2016, Chris Lattner <swift-evolution@swift.org> wrote:

--
Dave


(David Hart) #9

  * What is your evaluation of the proposal?

+1 for most of the proposal
-1 for the slicing subscripts

This proposal is well researched, and well written and makes total sense. I totally agree that a renaming is required and I agree with most of the proposal for the exception of the slicing subscripts. If the goal of the proposal was to make those APIs easily discoverable, then those subscripts go completely against that:

You need to know that the subscripts exist and if you are looking for a method, you will never find them.
They use a new type which is never used anywhere else.

On a more general note, creating new types in the Standard Library only to serve as arguments for those fairly rarely used subscripts sounds very heavy-weight.

I vote for keeping functions for those operations.

  * Is the problem being addressed significant enough to warrant a change to Swift?

Yes.

  * Does this proposal fit well with the feel and direction of Swift?

Yes, except for the subscripts, which look fairly alien to me.

  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

No.

  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Followed the original pitch, gave some feedback, re-read the proposal for the official review and spent a day pondering it.


(Pyry Jahkola) #10

Hello Swift community,

The review of "SE-0132: Rationalizing Sequence end-operation names" begins now and runs through July 26. Apologies for the short review cycle, but we’re right up against the end of source breaking changes for Swift 3. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md

  * What is your evaluation of the proposal?

+1 to the method renames.
-1 to the slice syntax proposed.

I find the introduction of unary operators confusing. I don't think it's good design that you need to remove a space when moving from the old syntax to the new one:

    array[array.startIndex ..< someIndex] // before
    array[..<someIndex] // after

and likewise, that you need to add parentheses here:

    array[array.startIndex ..< someIndex - 1] // before
    array[..<(someIndex - 1)] // after

OTOH, I would be okay if the proposal didn't implement unary operators but only the Optional<Index> overloads of `...` and `..<`:

    array[nil ..< someIndex] // first example
    array[nil ..< someIndex - 1] // second example

That said, maybe it's best to defer that part of the proposal until a later time and stick to prefix(upTo:), prefix(through:), and suffix(from:) for now.

  * Is the problem being addressed significant enough to warrant a change to Swift?

Yes.

  * Does this proposal fit well with the feel and direction of Swift?

Yes.

  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

N/A

  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick reading.

Thanks to Brent for the effort!

— Pyry

···

On 25 Jul 2016, at 09:10, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:


(Boris Wang) #11

I am curious about the reason using "removing",
What we are doing is not remove, just selecting some elements from the
original collection.


#12

* What is your evaluation of the proposal?

“It’s complicated”

First, I agree with the prevailing sentiment that the incomplete-range
portion ought to be separated and postponed.

Second, the proposed renaming of methods brings great consistency. However,
I believe that `first(n)` and `last(n)` read more clearly at the point of
use than `prefix(n)` and `suffix(n)`.

The best bikeshed labels I can devise for parameters on the latter are
`prefix(limit: n)` and `suffix(limit: n)`. That would narrow the gap in
readability, though not close it completely.

Moreover, `removingPrefix(limit: n)` and `removingSuffix(limit: n)` are
noticeably verboser (and still a bit less clear) than `removingFirst(n)`
and `removingLast(n)`.

        * Does this proposal fit well with the feel and direction of Swift?

Changing `drop` to `removing` certainly fits the API naming guidelines.

        * Is the problem being addressed significant enough to warrant a
change to Swift?

I am not sure. Rather, the drop-becomes-removing part is significant
enough, because the problem being solved is “method names don’t match the
API guidelines”, but I am not sure about the rest of the proposal.

        * If you have used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?

Regarding incomplete ranges, Matlab uses `end` as a placeholder for the end
index, so arr(6:end), and a middle number for step size, so arr(6:2:end).

I think that designing a range-with-stride system is worth considering in
the future, so we should not haphazardly introduce an incomplete-range
system which might unduly constrain the syntax.

        * How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?

Medium.

Nevin

···

On Mon, Jul 25, 2016 at 2:10 AM, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "SE-0132: Rationalizing Sequence end-operation names" begins
now and runs through July 26. Apologies for the short review cycle, but
we’re right up against the end of source breaking changes for Swift 3. The
proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md

Reviews are an important part of the Swift evolution process. All reviews
should be sent to the swift-evolution mailing list at

        https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and contribute to the direction of Swift.
When writing your review, here are some questions you might want to answer
in your review:

        * What is your evaluation of the proposal?
        * Is the problem being addressed significant enough to warrant a
change to Swift?
        * Does this proposal fit well with the feel and direction of Swift?
        * If you have used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?
        * How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?

More information about the Swift evolution process is available at

        https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Boris Wang) #13

My suggest:

* Change removing to exclude. For exam, S.excludeFirst
* Keep shorthand for first and last element
* Change pop to removeXXXCheck. For exam, C.popFirst =>C.removeFirstCheck

Charlie Monroe via swift-evolution <swift-evolution@swift.org>于2016年7月25日
周一15:45写道:

···

> On Jul 25, 2016, at 8:10 AM, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Hello Swift community,
>
> The review of "SE-0132: Rationalizing Sequence end-operation names"
begins now and runs through July 26. Apologies for the short review cycle,
but we’re right up against the end of source breaking changes for Swift 3.
The proposal is available here:
>
>
https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md
>
> Reviews are an important part of the Swift evolution process. All
reviews should be sent to the swift-evolution mailing list at
>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> or, if you would like to keep your feedback private, directly to the
review manager.
>
> What goes into a review?
>
> The goal of the review process is to improve the proposal under review
through constructive criticism and contribute to the direction of Swift.
When writing your review, here are some questions you might want to answer
in your review:
>
> * What is your evaluation of the proposal?

The updated API is more consistent and seems the right step to take.

> * Is the problem being addressed significant enough to warrant a
change to Swift?

Yes.

> * Does this proposal fit well with the feel and direction of Swift?

Yes.

> * If you have used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?
> * How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?

Read the proposal and skimmed through the discussion.

>
> More information about the Swift evolution process is available at
>
> https://github.com/apple/swift-evolution/blob/master/process.md
>
> Thank you,
>
> -Chris Lattner
> Review Manager
> _______________________________________________
> 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


(Boris Wang) #14

And try to use overloading more:

exclude(index: Int)
exclude(part: Range)
exclude({closure})

Boris Wang <kona.ming@gmail.com>于2016年7月25日 周一16:09写道:

···

My suggest:

* Change removing to exclude. For exam, S.excludeFirst
* Keep shorthand for first and last element
* Change pop to removeXXXCheck. For exam, C.popFirst =>C.removeFirstCheck

Charlie Monroe via swift-evolution <swift-evolution@swift.org>于2016年7月25日
周一15:45写道:

> On Jul 25, 2016, at 8:10 AM, Chris Lattner via swift-evolution < >> swift-evolution@swift.org> wrote:
>
> Hello Swift community,
>
> The review of "SE-0132: Rationalizing Sequence end-operation names"
begins now and runs through July 26. Apologies for the short review cycle,
but we’re right up against the end of source breaking changes for Swift 3.
The proposal is available here:
>
>
https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md
>
> Reviews are an important part of the Swift evolution process. All
reviews should be sent to the swift-evolution mailing list at
>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> or, if you would like to keep your feedback private, directly to the
review manager.
>
> What goes into a review?
>
> The goal of the review process is to improve the proposal under review
through constructive criticism and contribute to the direction of Swift.
When writing your review, here are some questions you might want to answer
in your review:
>
> * What is your evaluation of the proposal?

The updated API is more consistent and seems the right step to take.

> * Is the problem being addressed significant enough to warrant a
change to Swift?

Yes.

> * Does this proposal fit well with the feel and direction of
Swift?

Yes.

> * If you have used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?
> * How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?

Read the proposal and skimmed through the discussion.

>
> More information about the Swift evolution process is available at
>
> https://github.com/apple/swift-evolution/blob/master/process.md
>
> Thank you,
>
> -Chris Lattner
> Review Manager
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Dave Abrahams) #15

Hello Swift community,

The review of "SE-0132: Rationalizing Sequence end-operation names" begins
now and runs through July 26. Apologies for the short review cycle, but
we’re right up against the end of source breaking changes for Swift 3. The
proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md

Reviews are an important part of the Swift evolution process. All reviews
should be sent to the swift-evolution mailing list at

        https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and contribute to the direction of Swift.
When writing your review, here are some questions you might want to answer
in your review:

        * What is your evaluation of the proposal?

The end product is really quite beautiful. This proposal very much succeeds
at rationalizing all names without introducing terminology that's
unprecedented. I very much appreciate that properties or functions named
`first` now always return one or zero elements and that ranges are always
indicated with range operators. The only quibble is that `hasPrefix(_:by:)`
might be more appropriately `hasPrefix(_:comparingBy:)`, as the standalone
preposition doesn't quite make sense IMO.

It makes as much sense as in `elementsEqual(_:by:)` et. al, IMO.

···

on Sun Jul 24 2016, Xiaodi Wu <swift-evolution@swift.org> wrote:

On Mon, Jul 25, 2016 at 1:10 AM, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:

        * Is the problem being addressed significant enough to warrant a
change to Swift?

Yes.

        * Does this proposal fit well with the feel and direction of Swift?

Yes, very much so.

        * If you have used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?

This proposal rationalizes Swift-specific names, so not sure how to compare
to other languages.

        * How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?

A quick reading of this version; I followed the original discussion and
thought about it more in-depth then.

More information about the Swift evolution process is available at

        https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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

--
Dave


(Garth Snyder) #16

A warm -0.5 from me, meaning that I agree that this issue is important to address, and that I concur with many of the particulars. (I agree that removing is way better than drop, for example.)

However, I suspect that this approach may open more Pandora’s boxes than it closes. Most of my discomfort centers on the idea of incomplete ranges. This seems like a major thing to introduce to the language, and although I like the concept, it raises questions that deserve to be addressed independently. Incomplete ranges shouldn’t be allowed to just sneak in the back door as part of an API cleanup in one particular area.

Really, it seems like ranges could do with some attention in their own right. In their current form, they feel like largely pragmatic constructions that aren't yet fully baked. A lot of elaborations have already been piled onto them, and they’re responsible for a lot of API-level complexity. But as yet, they’re not capable of representing, e.g., intervals that are open or half-open on the left.

Incomplete ranges seem to push the idea of ranges even further in the direction of abstract predicates or mathematically general models of intervals. For example, SE-0132 suggests

    ..< 3

as a syntax for an upper-bounded range. But wouldn’t

   < 3

be a more natural syntax for this? From a logical perspective, you’re not really creating a range so much as expressing the condition that something is less than 3, which happens to be encoded by a range. If we had incomplete ranges, wouldn’t you want to be able to use them naturally in, e.g., case statements?

    switch (value) {
        case < 3:
            // Small value
        case 3…5:
            // Medium value
        case > 5:
            // Large value
    }

Doesn’t it seem odd that < 3 is representable as a range but > 5 is not?

(I’ve shown all these with reasonable spacing, but IIRC, the odd no-space-around-range-operators rule is still in effect. That would be worth addressing, too, if ranges are really going to be first-class citizens.)

Matthew Johnson: I think this proposal pushes a bit too hard on consistency of “first / last” vs “prefix / suffix”. Specifically, I think first(n) and last(n) are significantly more clear than prefix(n) and suffix(n) (and removingFirst(n) / removingLast(n) instead of removingPrefix(n) / removingSuffix(n). I think the former are immediately clear. The latter, while consistent in terms of matching semantics with a naming convention suffer in terms of clarity at the call site. I do not think it is immediately clear that the parameter specifies the *length* of the prefix or suffix.

Agreed. According to the API standards, a method removePrefix() seem like it should accept a prefix as an argument.

Matthew Johnson: Another comment is that you have generally moved index based methods to subscripts rather than named methods. Why didn’t you take this all the way and change `replaceSubrange` to be a subscript setter?

That’s a good point also.

  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A complete reading of the proposal, but without existing knowledge of the subtleties of Range implementation.

Garth


(Matthew Johnson) #17

  https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md

First, a big thanks to Brent for all the work in categorizing, describing, and justifying all these changes! I'm largely in favor of the new method names—several of the renamings lead to better grouping of related methods, and I think the compromises that have been made make lots of sense.

However, I'm not in favor of replacing the existing slicing methods with new subscript syntax that uses partial ranges. These represent a significant new vernacular for Swift that none of us has used before in the language. I'm additionally worried about the discoverability and clarity of this kind of slicing. In the other languages I've used that support similar kinds of subscripting, it always seems too clever by half—fussy to write and frequently confusing to read.

At this late date, I propose keeping the slicing methods (i.e., prefix(upTo:), prefix(through:), and suffix(from:)) and considering adopting the new slicing-via-subscript syntax in a future proposal.

This seems like a reasonable alternative. The slicing / subscript syntax can reasonably be viewed as sugar for the named methods.

···

On Jul 25, 2016, at 3:22 PM, Nate Cook via swift-evolution <swift-evolution@swift.org> wrote:

Best,
Nate

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


(Daniel Duan) #18

Please read the naming section in Swift API design guidelines https://swift.org/documentation/api-design-guidelines/#naming

Daniel Duan

···

Sent from my iPhone

On Jul 25, 2016, at 8:29 PM, Boris Wang via swift-evolution <swift-evolution@swift.org> wrote:

I am curious about the reason using "removing",
What we are doing is not remove, just selecting some elements from the original collection.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #19

> Hello Swift community,
>
> The review of "SE-0132: Rationalizing Sequence end-operation names"
> begins now and runs through July 26. Apologies for the short review
> cycle, but we’re right up against the end of source breaking changes
> for Swift 3. The proposal is available here:
>
>
https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md
>
> Reviews are an important part of the Swift evolution process. All
> reviews should be sent to the swift-evolution mailing list at
>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> or, if you would like to keep your feedback private, directly to the
review manager.
>
> What goes into a review?
>
> The goal of the review process is to improve the proposal under review
> through constructive criticism and contribute to the direction of
> Swift. When writing your review, here are some questions you might
> want to answer in your review:
>
> * What is your evaluation of the proposal?

I'm mostly very much in favor of this proposal, but I have some
thoughts.

First, though, I have to apologize for those wide tables, since I'm
listed as a co-author (because of a small design contribution). The
only way I've been able to read them is by looking at the markdown
source, so that's how I'm going to quote it here.

[Note to future authors: if you need to include a table, this is how you
can make it narrow enough:

https://github.com/apple/swift-evolution/blob/master/proposals/0118-closure-parameter-names-and-labels.md#proposed-solution
.
The source is awful to read but it renders OK.]

> ## Proposed solution
>
> We sever the index-taking APIs from the others, forming two separate
> families, which we will call the "Sequence-end operations" and the
> "index-based operations". We then consider and redesign them along
> separate lines.
>
> ### Sequence-end operations
>
> Each of these APIs should be renamed to use a directional word based on
> its row in the table:
>
> > Operand | Directional word |
> > -------------------------------- | ------------------ |
> > **Fixed Size** |
> > First 1 | first |
> > Last 1 | last |
> > First (n: Int) | prefix |
> > ...with closure | prefix |
> > Last (n: Int) | suffix |
> > ...with closure | suffix |
> > **Searching From End** |
> > First matching element | first |
> > ...with closure | first |
> > Last matching element | last |
> > ...with closure | last |
>
> To accomplish this, `starts(with:)` should be renamed to
> `hasPrefix(_:)`, and other APIs should have directional words replaced
> or added as appropriate.
>
> Additionally, the word `drop` in the "Exclude" APIs should be replaced
> with `removing`. These operations omit the same elements which the
> `remove` operations delete, so even though the types are not always
> the same (`removing` returns `SubSequence`, not `Self`), we think they
> are similar enough to deserve to be treated as nonmutating forms.

Unfortunately there's a semantic difference that I hadn't noticed
before: the mutating “remove” operations have a precondition that there
be at least as many elements as are being removed. “Drop,” like “pop,”
is forgiving of such overruns. I think this is solvable; my suggestion
is below

> These changes yield (altered names **bold**):
>
> > > Get |
Index | Exclude
       > Remove (1) | Pop (1) | Equate (2)
               >
> > -------------------------------- | ----------------------------- |
------------------------------------- |
----------------------------------------- | --------------------- |
------------ | ------------------------------------------ |
> > **Fixed Size** |
> > First 1 | C.first | -
                                   > **S.removingFirst()**
   > C.removeFirst() | C.popFirst() | -
            >
> > Last 1 | C.last | -
                                   > **S.removingLast()**
    > C.removeLast() | C.popLast() | -
            >
> > First (n: Int) | S.prefix(3) | -
                                   > **S.removingPrefix(3)**
   > **C.removePrefix(3)** | - | **S.hasPrefix([x,y,z])**
           >
> > ...with closure | S.prefix(while: isPrime) | -
                                   > **S.removingPrefix(while:
isPrime)** | - | - | **S.hasPrefix([x,y,z],
    by: ==)** |
> > Last (n: Int) | S.suffix(3) | -
                                   > **S.removingSuffix(3)**
   > **C.removeSuffix(3)** | - | -
            >
> > ...with closure | - | -
                                   > -
   > - | - | -
            >
> > **Searching From End** |
> > First matching element | - |
**C.firstIndex(of: x)** | -
       > - | - | -
                >
> > ...with closure | S.first(where: isPrime) |
**C.firstIndex(where: isPrime)** | -
       > - | - | -
                >
> > Last matching element | - | -
                                   > -
   > - | - | -
            >
> > ...with closure | - | -
                                   > -
   > - | - | -
            >

My suggestion would be to make the remove()
operations more forgiving:

   rename popFirst() to removeFirst()
   rename popLast() to removeLast()

   kill removeFirst(n)
   kill removeLast(n)

+1 to this.

The “forgiving” forms of x.removeFirst(n) and x.removeLast(n) can be
expressed as:

   let i = x.index(x.startIndex, offsetBy: n, limitedBy: x.endIndex)
   x.removeSubrange(..<i)

   let i = x.index(x.endIndexIndex, offsetBy: -n, limitedBy: x.startIndex)
   x.removeSubrange(i..<)

I realize that's quite verbose. We could of course just make
removePrefix(n) and removeSuffix(n) forgiving, but I have long believed
that the “prefix/suffix” methods should go one of two ways:

a. they get a label that clarifies the meaning of the argument, e.g.

   x.removePrefix(ofMaxLength: n)
   x.removeSuffix(ofMaxLength: n)

b. they are given a recognizable domain-specific notation such as:

   x.removeSubrange($+n..<)
   x.removeSubrange(..<$-n)

   I am strongly in favor of this answer (which is implementable within
   the framework of this proposal) because of the way it reduces API
   surface area and leverages the user's understanding of how ranges
   work.

   It also implies we can replace

     x.removingPrefix(n)
     x.removingSuffix(n)

   with

     x[$+n..<]
     x[..<$-n]

  for Collections.

I'm not enamored of this suggestion. It succeeds in reducing API surface
area, but at a severe cost to readability. You'd replace an unambiguous
phrase (removing prefix or suffix), the meaning of which is further
clarified by the consistent usage proposed in SE-0132, with a wordless
spelling using some combination of [$+.<]. Cognitively, also, it
substantially increases the burden for the reader: it replaces a single
argument with a nested series of arguments; first, one must understand the
meaning of the $ placeholder, then one must consider an addition or
subtraction operation, then the formation of a range, and in the last
example, the use of that range as a subscript argument--again, all
wordlessly. Finally, subscripts have so far not been "forgiving," while
today's `dropLast` very much is; this suggestion would add inconsistency by
using a subscript for a forgiving or "lenient" operation.

  That would admittedly leave single-pass Sequences without an API for
  dropping the first N elements. I am inclined to think that interface
  should be moved to Iterator.

  The introduction of such notation raises the question of whether we
  need unary range operators, and could instead go with

     x[i..<$] and x[$..<i]

  which is after all only one character longer than

     x[i..<] and x[..<i]

  and stays out of the territory of the prefix/suffix “pack/unpack”
  operators that are likely to be used for generic variadics.

Pyry's comments about the precedence issues with unary range operators are,
I think, serious enough that the unary operators should be reconsidered.
This suggestion might work but I wonder if we could do better than $.

···

On Mon, Jul 25, 2016 at 8:35 PM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Sun Jul 24 2016, Chris Lattner <swift-evolution@swift.org> wrote:

> ### Index-based operations
>
> Because these APIs look up elements based on their indices, we believe
> these operations should be exposed as subscripts, and ideally should
> look like other slicing operations:
>
> ```swift
> let head = people[..<i]
> let tail = people[i..<]
> let rearrangedPeople = tail + head
> ```
>
> <!-- Comment to make my editor happy -->
>
> We will accomplish this by introducing two new types, `IncompleteRange`
> and `IncompleteClosedRange`. These are similar to `Range` and
> `ClosedRange`, except that the bounds are optional.
>
> To construct them, we will introduce both prefix and suffix operators
> taking a non-optional bound, and infix operators taking optional bounds.
> (We offer both because `c[..<i]` is more convenient than `c[nil ..< i]`,
> but doesn't allow you to dynamically choose between supplying and
> omitting a bound.) These will follow the existing convention: `..<` will
> construct the half-open `IncompleteRange`, while `...` will construct
> `IncompleteClosedRange`.

I believe the `$+n..<i` idea is still implementable with these basic
types, just with an enum instead of optionals. I'll take a shot at it
tonight if I can get a few minutes.

> Rather than continuing to proliferate overloads of slicing subscripts,
> we will also introduce a new `RangeExpression` protocol which allows
> any range-like type to convert itself into a plain `Range<Index>`
> appropriate to the collection in question. Thus, there should only be
> two range subscripts: one taking `Range<Index>`, and one taking
> everything else.
>
> We will also modify the existing `removeSubrange(_:)` and
> `replaceSubrange(_:with:)` calls to take `RangeExpression` instances,
> thereby merging many existing variants into one while simultaneously
> extending them to support `IncompleteRange` and `IncompleteClosedRange`.
> Though technically additive, we believe this is an easy win.
>
> Thus, the table above becomes:
>
> > > Type
              > Get | Remove
> Replace |
> > ------------------------------------------------ |
--------------------------------- | -------------------- |
----------------------------------- |
----------------------------------------------------------- |
> > **Based on Index, Arbitrary** |
> > (i: Index) ..< (j: Index) | Range\<Index>
               > C[i ..< j] | C.removeSubrange(i ..< j)
> C.replaceSubrange(i ..< j, with: [x,y]) |
> > ...Countable |
CountableRange\<Index> | C[i ..< j] |
C.removeSubrange(i ..< j) | C.replaceSubrange(i ..< j,
    with: [x,y]) |
> > (i: Index) ... (j: Index) |
ClosedRange\<Index> | C[i ... j] |
C.removeSubrange(i ... j) | C.replaceSubrange(i ... j, with:
[x,y]) |
> > ...Countable |
CountableClosedRange\<Index> | C[i ... j] |
C.removeSubrange(i ... j) | C.replaceSubrange(i ... j, with:
[x,y]) |
> > **Based on Index, From End** |
> > startIndex ..< (i: Index) |
**IncompleteRange\<Index>** | **C[..\<i]** |
**C.removeSubrange(..\<i)** | **C.replaceSubrange(..\<i,
with: [x,y])** |
> > (i: Index) ..< endIndex |
**IncompleteRange\<Index>** | **C[i..\<]** |
**C.removeSubrange(i..\<)** | **C.replaceSubrange(i..\<, with:
[x,y])** |
> > startIndex ... (i: Index) |
**IncompleteClosedRange\<Index>** | **C[...i]** |
**C.removeSubrange(...i)** | **C.replaceSubrange(...i, with:
[x,y])** |
>
> However, it should be implemented with merely:
>
> > > Type
                              > Get | Remove
                > Replace
   >
> > --------------------------------------------- |
--------------------------------------------------- | --------------------
> ----------------------------------- |
----------------------------------------------------------- |
> > (i: Index) ..< (j: Index) | Range\<Index>
                             > C[i ..< j] | C.removeSubrange(i
  ..< j) | C.replaceSubrange(i ..< j, with: [x,y])
>
> > Everything else | RangeExpression where
    Bound == Index | C[i ... j] | C.removeSubrange(i
  ... j) | C.replaceSubrange(i ... j, with: [x,y])
>
>
> ## Detailed design
>
> ### Sequence-end operations
>
> The following methods should be renamed as follows wherever they appear
> in the standard library. These are simple textual substitutions; we
> propose no changes whatsoever to types, parameter interpretations, or
> other semantics.
>
> > Old method | New method
                                      >
> > ------------------------------------------------- |
------------------------------------------------------- |
> > `dropFirst() -> SubSequence` | `removingFirst()
-> SubSequence` |
> > `dropLast() -> SubSequence` | `removingLast() ->
SubSequence` |
> > `dropFirst(_ n: Int) -> SubSequence` | `removingPrefix(_
n: Int) -> SubSequence` |
> > `drop(@noescape while predicate: (Iterator.Element) throws -> Bool)
rethrows -> SubSequence` | `removingPrefix(@noescape while predicate:
(Iterator.Element) throws -> Bool) rethrows -> SubSequence` |

I'm concerned with how the above fits into the scheme. Writing it out
is:

    x[(x.firstIndex(where: {!predicate($0)}) ?? x.endIndex)..<$]

and that's just for Collections. Drat; we might not be able to get rid
of these “removingPrefix” operations altogether. OK, I'm out of time so
I'll have to get back to this.

--
Dave

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


(Brent Royal-Gordon) #20

I find the introduction of unary operators confusing. I don't think it's good design that you need to remove a space when moving from the old syntax to the new one:

    array[array.startIndex ..< someIndex] // before
    array[..<someIndex] // after

and likewise, that you need to add parentheses here:

    array[array.startIndex ..< someIndex - 1] // before
    array[..<(someIndex - 1)] // after

OTOH, I would be okay if the proposal didn't implement unary operators but only the Optional<Index> overloads of `...` and `..<`:

    array[nil ..< someIndex] // first example
    array[nil ..< someIndex - 1] // second example

The proposal includes both infix and prefix/postfix forms because the tradeoff between the two is rather complex.

The advantages of infix are:

* Closer to the non-incomplete Range syntax
* Permits spacing for readability
* Allows for `Index?` bounds, so you can choose at runtime whether or not to provide a particular bound
* Doesn't take leading or trailing `...`, which we may want for other purposes
* Doesn't require new operators to be declared

The advantages of prefix and postfix are:

* Don't add overloads (and thus type checker complexity) on existing ranges
* Don't use `nil` for "choose the end", which might be a little opaque
* Won't become potentially ambiguous if Optional becomes Comparable later

I think both syntaxes have a role to play, with the prefix and postfix syntaxes being useful for simple situations while infix can help with more complex ones. But I also think that it's a complex enough question that it's better to present both to the reviewers and core team so that more people can weigh in. I anticipated that one set might not survive review, and the design would be fine with only prefix/postfix, only infix, or both.

That said, maybe it's best to defer that part of the proposal until a later time and stick to prefix(upTo:), prefix(through:), and suffix(from:) for now.

This is also an option. I hoped to make it in before Swift 3 so we could avoid a deprecation cycle, but the two parts *are* essentially severable.

···

On Jul 25, 2016, at 1:46 PM, Pyry Jahkola via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies