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

"When the operation is naturally described by a verb, use the verb’s
imperative for the mutating method and apply the “ed” or “ing” suffix to
name its nonmutating counterpart."

I known it. But, the "verb" here should be a action will change the object.
Not every verb will change the object.

Like the word: peek ,copy.

In socket program, there's a action "peek", just check if there has new
data in socket data buffer, not move out the data(like the API receive()).
It's not "receiving".

Daniel Duan <daniel@duan.org>于2016年7月26日 周二11:39写道:

···

Please read the naming section in Swift API design guidelines
Swift.org - API Design Guidelines

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

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.

Sorry about that. I felt that the systematic way the names were arranged couldn't be conveyed in any other way, but the resulting formatting is regrettably difficult to read.

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

Wow, there's a lot going on in this next section.

To preface this discussion, I'll merely point out that, in writing this proposal, I tried to narrowly focus on renaming. My inability to rename `prefix(upTo/through:)` and `suffix(from:)` in a way that made sense led me to a more aggressive redesign, but for the other calls I considered larger redesigns out of scope. I say this not as an argument against redesigning the `remove` calls, but merely to explain why I didn't fiddle with their semantics already.

To address things one at a time:

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

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

I actually quite agree that `removeFirst/Last()` and `popFirst/Last()` are redundant. I think I briefly touched on that in "Future Directions".

Going out of order for a moment:

We could of course just make
removePrefix(n) and removeSuffix(n) forgiving,

If we're going to go beyond renaming, this is the approach I favor, because it is the most straightforward and readable. When you see a piece of code that says `removePrefix(n)`, it doesn't take much effort to figure out that it's removing a series of elements from the beginning of the sequence.

Specifically, if we go this route, I would:

* Remove the `popFirst/Last` methods.
* Remove the preconditions on removeFirst/Last/Prefix/Suffix requiring the collection to not be smaller than the number of elements to be removed.
* Change the signatures to something like:

  @discardableResult
  func removeFirst() -> Iterator.Element?
  @discardableResult
  func removeLast() -> Iterator.Element?
  @discardableResult
  func removePrefix(_ n: Int) -> [Iterator.Element] // or SubSequence
  @discardableResult
  func removeSuffix(_ n: Int) -> [Iterator.Element] // or SubSequence

I've added return types to `removePrefix/Suffix` so that they follow our general rule about not throwing away information that isn't necessarily easy to compute, but that part of the change isn't strictly necessary.

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)

I struggled for a while to find a good label for the count parameters, but couldn't find a good convention.

The fundamental problem is that English counting grammar doesn't line up with Swift syntax. In English, you would like to say "prefix of 3 elements", but you can't put a label after a parameter in Swift. Thus, there may not be an idiomatic way to label these parameters. (This is a problem throughout the standard library, actually; I ran into the same issues when I was looking at the UnsafeRawPointer proposal.)

Labels like `ofMaxLength` are verbose, and the use of "length" in particular isn't great when the standard library otherwise uses "count". If you put a gun to my head and demanded I add a label, I would make it `x.removePrefix(ofUpTo: n)` or just `x.removePrefix(of: n)`. But I'm not convinced any of these bring enough to the table. When you see an unlabeled Int parameter to a call with a name like `removePrefix`, there's really only a couple of things it could mean, and the count is an unsurprising one.

On the other hand, the internal parameter name should probably not be `n`; it should be something like `count` or even `countToRemove`.

(Probably obvious, but worth mentioning explicitly: if we add a label, we should apply it consistently to all `prefix` and `suffix` calls which take a count.)

Okay, back to the right order:

  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.

No kidding. It requires:

* Two statements, four references to the collection, and 88 characters (for an example with one-letter variable names)
* The use of a complex index-manipulation function
* The simultaneous use of the startIndex, endIndex, and a sign on the count, all of which must be coordinated

I think that, if `removePrefix/Suffix(_:)` were not in the standard library, people would be forced to invent them.

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

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

Does $ represent the start, the end, or either one depending on which side of the range we're on? Because if it's the third option, I think these two operations are actually inverted: the first is removing everything *except* the `prefix(n)`, and the second is removing everything except the `suffix(n)`.

(Or maybe it's based on the sign, with a negative offset going from the endIndex, and a positive offset going from the startIndex? Don't we usually try to avoid that kind of runtime branching?)

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.

Why would it be a good thing to use two different syntaxes for the same operation?

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.

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.

Xcode 8 beta 3-compatible hack based on my assumptions about how this supposed to work, suitable for at least testing the ergonomics: https://gist.github.com/brentdax/3c5c64d3b7ca3ff6b68f1c86163c39c4

At a technical level, I think the biggest problem is the `$` symbol. If it's a constant or variable, then it can't be generic, and we need to fix a specific SignedInteger type for the offset. That means we need to convert to whatever IndexDistance ends up being. That could be fixed if either `$` became a magic syntax or we get generic constants, but either of those will be a significant effort. Or it can be a function, but then it's less convenient.

Stepping back from implementation, though, I think the `$` syntax is just too opaque. It gets better when it's spaced out:

  array[$ + 2 ..< $ - 1]

But it's still not something whose meaning you will even make a wild guess at without looking it up. Even once you learn it, the $ symbol will almost always be squeezed between two other punctuation characters, making visual parsing more difficult. The effect is not unlike stereotypes of Perl.

(Note: I'm an old Perl hand, so others might better read that sentence as "The effect is not unlike Perl.")

I mean, look at this example you gave earlier:

  It also implies we can replace

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

  with

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

for Collections.

The first version is clear as crystal; the second is clear as mud. The first version gives the parameter a position of prominence; the second buries it in the middle of a complex expression.

Now, abandoning the `$` could help here. I've worked up an alternate design which uses descriptive names at <https://gist.github.com/brentdax/0946a99528f6e6500d93bbf67684c0b3&gt;\. The example I gave above is instead written:

  array[.startIndex + 2 ..< .endIndex - 1]

Or, for the removingPrefix equivalent:

  x[.startIndex + n ..< .endIndex]

But this is longer than a removingPrefix call and *still* buries the intent. And adding prefix/suffix operators back in doesn't really help; it merely allows you to omit the straightforward bits, without doing anything to help make the non-straightforward bits more clear. (And in fact, it makes those worse by forcing you to either compress the whitespace in them or parenthesize.)

And even if we get past the syntax issues, there's an attractive nuisance problem. This feature, whatever it is, will undoubtedly circulate as "the way to make String indexes work". Is that something we want to bring to the language?

In short, I think the idea of relative ranges is pretty neat. It's brief, it's parsimonious, it's flexible, it's clever. But it makes code less clear and it hides performance problems. I think either of those two factors by itself ought to make us rethink a Swift proposal.

···

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

--
Brent Royal-Gordon
Architechies

FWIW, I don't see any evidence that such a sentiment prevails.

tail-wagging-the-dog?-ly y'rs,

···

on Tue Jul 26 2016, Nevin Brackett-Rozinsky <nevin.brackettrozinsky-AT-gmail.com> wrote:

        * 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.

--
Dave

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}(_:) → 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?

+1

There is a use case for it, but IMO it's a corner that doesn't need
terse syntax.

I don't agree that "drop" sounds particularly scary,

It's scary because it's active. “Dropping” has other issues, as you
note below.

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.

+1

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.

Me too. That was the motivation for my suggestion of x[..<$+n], meaning
x.prefix(n).

However, that proposal is a failure (I just wrote it wrong above again
before correcting myself)!

The main point of changing prefix(to:) and suffix(from:) into x[..<i]
and x[i..<] is not to make them subscripts, but to re-use the cognitive
power users have already invested in range expressions. Writing them as
something like x.slice(..<i) and x.slice(i..<), would accomplish that,
too.

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).

It collapses a whole raft of overloads into two (except for subscript,
because we don't have generic subscript yet, which is a temporary
limitation). In general if you want to implement a collection method
that takes a range today, you really should implement four overloads.
With the introduction of the incomplete ranges it's six or eight.

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?

For the purposes of creating a range of valid indices in a collection, yes.

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?

We don't.

...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(_:) 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: slice - How slicing in Python works - Stack Overflow
    Mathematica: Span—Wolfram Language Documentation

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]".

There's a point at which we should stop trying to overload subscript
syntax and just use stride(...).

They also both support *negative indices *which count from the end.

That's another thing I was getting at with x[..<$-n]
(a.k.a. x.removingSuffix(ofMaxLength: n)). But again, #FAIL.

···

on Mon Jul 25 2016, Jacob Bandes-Storch <swift-evolution@swift.org> wrote:

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&gt;\) 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.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave

However, I believe that `first(n)` and `last(n)` read more clearly at the point of use than `prefix(n)` and `suffix(n)`.

I've seen this a couple of times. It's something I bring up in "Other alternatives":

* We considered using `first` and `last` as the basis for both
  single-element and multiple-element operations (such that `prefix(3)`
  would become `first(3)`, etc.), but:
  
  1. These seemed like distinct functionalities, particularly since
     their types are different.
  
  2. We're not comfortable with heavily overloading a property with a
     bunch of methods, and didn't want to make `first` and `last` into
   methods.
  
  3. Most APIs work fine, but `hasFirst(_:)` is atrocious, and we see
     no better alternative which includes the word `first`.

To give you an idea of what I mean by #3:

  if numbers.hasFirst([1, 2, 3, 4, 5]) && numbers.hasLast([5, 4, 3, 2, 1]) { … }

Keeping those issues in mind, do you still prefer `first(n)` over `prefix(n)`?

···

On Jul 26, 2016, at 8:30 AM, Nevin Brackett-Rozinsky <nevin.brackettrozinsky@gmail.com> wrote:

--
Brent Royal-Gordon
Architechies

  * 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.

+1, and it doesn't: it specifies the *maximum length*.

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?

The language doesn't allow it:

1. replaceSubrange is generic on the sequence argument and we don't have
   generic subscript

2. you'd need to have a matching subscript getter, and there would be no
   way to deduce the result in most cases, potentially causing
   ambiguity. I dunno, maybe this could work, but we'd need to solve
   problem 1 before we could find out.

···

on Mon Jul 25 2016, Matthew Johnson <swift-evolution@swift.org> wrote:

  * 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

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

--
Dave

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

I got to add that while I do like the renaming section of the proposal I do have reservations about RangeExpression and IncompleteRange.

I think these additions will make index based operations harder to discover by autocomplete. Unnecessary feels like ranges would get much complicated and I feel that is a regression from the simplification treatment that they just received. They don't feel swifty to me. :)

-1 for IncompleteRange
-1 collection[to/through/from] // don't like this much.
-1 for RangeExpression

Perhaps something similar could be introduced later but I think the proposal just stick to just renaming like it claims in the scope section.

···

On Jul 25, 2016, at 8:17 AM, Jose Cheyo Jimenez <cheyo@masters3d.com> wrote:

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

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.

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

I double Nevin on everything he said, especially about incomplete ranges.

···

2016-07-26 21:13 GMT+03:00 Dave Abrahams via swift-evolution < swift-evolution@swift.org>:

on Tue Jul 26 2016, Nevin Brackett-Rozinsky < > nevin.brackettrozinsky-AT-gmail.com> wrote:

> * 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.

FWIW, I don't see any evidence that such a sentiment prevails.

tail-wagging-the-dog?-ly y'rs,

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

There’s remove, which is mutating in that it actually removes the elements from the target. removing, on the other hand is nonmutating and basically gives a copy and then removes from the copy.
Saagar Jha

···

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

"When the operation is naturally described by a verb, use the verb’s imperative for the mutating method and apply the “ed” or “ing” suffix to name its nonmutating counterpart."

I known it. But, the "verb" here should be a action will change the object. Not every verb will change the object.

Like the word: peek ,copy.

In socket program, there's a action "peek", just check if there has new data in socket data buffer, not move out the data(like the API receive()). It's not "receiving".

Daniel Duan <daniel@duan.org <mailto:daniel@duan.org>>于2016年7月26日 周二11:39写道:
Please read the naming section in Swift API design guidelines Swift.org - API Design Guidelines

Daniel Duan
Sent from my iPhone

On Jul 25, 2016, at 8:29 PM, Boris Wang via swift-evolution <swift-evolution@swift.org <mailto: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 <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

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.

Sorry about that. I felt that the systematic way the names were
arranged couldn't be conveyed in any other way, but the resulting
formatting is regrettably difficult to read.

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

Wow, there's a lot going on in this next section.

Yeah, my turn to say sorry.

To preface this discussion, I'll merely point out that, in writing
this proposal, I tried to narrowly focus on renaming. My inability to
rename `prefix(upTo/through:)` and `suffix(from:)` in a way that made
sense led me to a more aggressive redesign, but for the other calls I
considered larger redesigns out of scope. I say this not as an
argument against redesigning the `remove` calls, but merely to explain
why I didn't fiddle with their semantics already.

Makes sense.

To address things one at a time:

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

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

I actually quite agree that `removeFirst/Last()` and `popFirst/Last()`
are redundant. I think I briefly touched on that in "Future
Directions".

Missed that. Your proposal is a bit long, so it's hard to take
everything in. Here's what you say:

   removeFirst/Last() and popFirst/Last() are very nearly redundant;
   their only difference is that the remove methods have a non-Optional
   return type and require the collection not be empty, while the pop
   methods have an Optional return type and return nil if it's empty.

   These operations could be merged, with the remove operations taking
   on the preconditions of the current pop operations;

The current pop operations *have* no preconditions. By “taking on the
preconditions of the current pop operations” do you mean “dropping their
preconditions?”

   additionally, removePrefix(_:) and removeSuffix(_:) could drop their
   equivalent preconditions requiring that the elements being removed
   exist.

IIUC you are suggesting making the remove operations uniformly
“forgiving,” yes? I think I'm in favor.

Going out of order for a moment:

We could of course just make
removePrefix(n) and removeSuffix(n) forgiving,

If we're going to go beyond renaming, this is the approach I favor,
because it is the most straightforward and readable. When you see a
piece of code that says `removePrefix(n)`, it doesn't take much effort
to figure out that it's removing a series of elements from the
beginning of the sequence.

Of course not, that's entirely captured by “removePrefix.” It's the
clarity of the meaning of “n” that concerns me.

Specifically, if we go this route, I would:

* Remove the `popFirst/Last` methods.
* Remove the preconditions on removeFirst/Last/Prefix/Suffix requiring
the collection to not be smaller than the number of elements to be
removed.
* Change the signatures to something like:

  @discardableResult
  func removeFirst() -> Iterator.Element?
  @discardableResult
  func removeLast() -> Iterator.Element?

This part is equivalent to renaming popFirst/ popLast to
removeFirst/removeLast, right?

  @discardableResult func removePrefix(_ n: Int) ->
  [Iterator.Element] // or SubSequence
      @discardableResult func
  removeSuffix(_ n: Int) -> [Iterator.Element] // or SubSequence

I've added return types to `removePrefix/Suffix` so that they follow
our general rule about not throwing away information that isn't
necessarily easy to compute, but that part of the change isn't
strictly necessary.

Yeah, I don't think we can do those without loss of efficiency; the
return value would prevent removals from being done in-place in CoW
structures.

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)

I struggled for a while to find a good label for the count parameters,
but couldn't find a good convention.

The fundamental problem is that English counting grammar doesn't line
up with Swift syntax. In English, you would like to say "prefix of 3
elements", but you can't put a label after a parameter in Swift. Thus,
there may not be an idiomatic way to label these parameters. (This is
a problem throughout the standard library, actually; I ran into the
same issues when I was looking at the UnsafeRawPointer proposal.)

I don't see how “a prefix of maximum length 3” fails to be idiomatic.

Labels like `ofMaxLength` are verbose,

Yes.

and the use of "length" in particular isn't great when the standard
library otherwise uses "count".

The name `count` causes all kinds of problems, so I view conflicts with
it as inevitable.

If you put a gun to my head and demanded I add a label, I would make
it `x.removePrefix(ofUpTo: n)`

That one is easily misinterpreted as removing the longest prefix that
doesn't include the value `n`.

or just `x.removePrefix(of: n)`.

That one is easily misinterpreted as removing the first element if it is
equal to `n`.

But I'm not convinced any of these bring enough to the table.

I agree that they don't clarify enough (or anything in the case of “of”).

When you see an unlabeled Int parameter to a call with a name like
`removePrefix`, there's really only a couple of things it could mean,
and the count is an unsurprising one.

On the other hand, the internal parameter name should probably not be
`n`; it should be something like `count` or even `countToRemove`.

The parameter name should first serve the documentation comment. IMO
`count` is worse than `n` in that regard and `countToRemove` makes the
comment read awkwardly. But this is really tangential at best.

(Probably obvious, but worth mentioning explicitly: if we add a label,
we should apply it consistently to all `prefix` and `suffix` calls
which take a count.)

Obvious, yes.

Okay, back to the right order:

  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.

No kidding. It requires:

* Two statements, four references to the collection, and 88 characters
(for an example with one-letter variable names)
* The use of a complex index-manipulation function
* The simultaneous use of the startIndex, endIndex, and a sign on the
count, all of which must be coordinated

I think that, if `removePrefix/Suffix(_:)` were not in the standard
library, people would be forced to invent them.

They might, but probably not in that form. I'm not convinced people
need them to be forgiving. They just have to be forgiving if we name
them the same as the renamed popXXX methods. But that doesn't change
your point substantially.

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

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

Does $ represent the start, the end, or either one depending on which
side of the range we're on? Because if it's the third option, I think
these two operations are actually inverted: the first is removing
everything *except* the `prefix(n)`, and the second is removing
everything except the `suffix(n)`.

Wow, that was impressive! With one stroke, you have just convinced me
that we can't do this. The fact that I got it wrong, along with other
excellent feedback in this thread, kills my interest in using $ in this
way.

(Or maybe it's based on the sign, with a negative offset going from
the endIndex, and a positive offset going from the startIndex? Don't
we usually try to avoid that kind of runtime branching?)

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.

Why would it be a good thing to use two different syntaxes for the
same operation?

It wouldn't. I was just trying to make the world fit my preconceived
idea of the right solution ;-)

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.

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.

Xcode 8 beta 3-compatible hack based on my assumptions about how this
supposed to work, suitable for at least testing the ergonomics:
https://gist.github.com/brentdax/3c5c64d3b7ca3ff6b68f1c86163c39c4

At a technical level, I think the biggest problem is the `$`
symbol. If it's a constant or variable, then it can't be generic, and
we need to fix a specific SignedInteger type for the offset. That
means we need to convert to whatever IndexDistance ends up being. That
could be fixed if either `$` became a magic syntax or we get generic
constants, but either of those will be a significant effort. Or it can
be a function, but then it's less convenient.

Stepping back from implementation, though, I think the `$` syntax is
just too opaque. It gets better when it's spaced out:

  array[$ + 2 ..< $ - 1]

But it's still not something whose meaning you will even make a wild
guess at without looking it up. Even once you learn it, the $ symbol
will almost always be squeezed between two other punctuation
characters, making visual parsing more difficult. The effect is not
unlike stereotypes of Perl.

(Note: I'm an old Perl hand, so others might better read that sentence
as "The effect is not unlike Perl.")

OK, now you're just driving the dagger in deeper. My poor idea is dead already!

I mean, look at this example you gave earlier:

  It also implies we can replace

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

  with

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

for Collections.

The first version is clear as crystal; the second is clear as mud. The
first version gives the parameter a position of prominence; the second
buries it in the middle of a complex expression.

Now, abandoning the `$` could help here. I've worked up an alternate
design which uses descriptive names at
<https://gist.github.com/brentdax/0946a99528f6e6500d93bbf67684c0b3&gt;\. The
example I gave above is instead written:

  array[.startIndex + 2 ..< .endIndex - 1]

Or, for the removingPrefix equivalent:

  x[.startIndex + n ..< .endIndex]

But this is longer than a removingPrefix call and *still* buries the
intent. And adding prefix/suffix operators back in doesn't really
help; it merely allows you to omit the straightforward bits, without
doing anything to help make the non-straightforward bits more
clear. (And in fact, it makes those worse by forcing you to either
compress the whitespace in them or parenthesize.)

And even if we get past the syntax issues, there's an attractive
nuisance problem. This feature, whatever it is, will undoubtedly
circulate as "the way to make String indexes work". Is that something
we want to bring to the language?

In short, I think the idea of relative ranges is pretty neat. It's
brief, it's parsimonious, it's flexible, it's clever. But it makes
code less clear and it hides performance problems. I think either of
those two factors by itself ought to make us rethink a Swift proposal.

Sold.

Thanks for indulging my unworkable fantasy,

···

on Tue Jul 26 2016, Brent Royal-Gordon <brent-AT-architechies.com> wrote:

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

--
Dave

Daniel Duan

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.

Sorry about that. I felt that the systematic way the names were arranged couldn't be conveyed in any other way, but the resulting formatting is regrettably difficult to read.

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

Wow, there's a lot going on in this next section.

To preface this discussion, I'll merely point out that, in writing this proposal, I tried to narrowly focus on renaming. My inability to rename `prefix(upTo/through:)` and `suffix(from:)` in a way that made sense led me to a more aggressive redesign, but for the other calls I considered larger redesigns out of scope. I say this not as an argument against redesigning the `remove` calls, but merely to explain why I didn't fiddle with their semantics already.

To address things one at a time:

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

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

I actually quite agree that `removeFirst/Last()` and `popFirst/Last()` are redundant. I think I briefly touched on that in "Future Directions".

Going out of order for a moment:

We could of course just make
removePrefix(n) and removeSuffix(n) forgiving,

If we're going to go beyond renaming, this is the approach I favor, because it is the most straightforward and readable. When you see a piece of code that says `removePrefix(n)`, it doesn't take much effort to figure out that it's removing a series of elements from the beginning of the sequence.

Specifically, if we go this route, I would:

* Remove the `popFirst/Last` methods.
* Remove the preconditions on removeFirst/Last/Prefix/Suffix requiring the collection to not be smaller than the number of elements to be removed.
* Change the signatures to something like:

   @discardableResult
   func removeFirst() -> Iterator.Element?
   @discardableResult
   func removeLast() -> Iterator.Element?
   @discardableResult
   func removePrefix(_ n: Int) -> [Iterator.Element] // or SubSequence
   @discardableResult
   func removeSuffix(_ n: Int) -> [Iterator.Element] // or SubSequence

I've added return types to `removePrefix/Suffix` so that they follow our general rule about not throwing away information that isn't necessarily easy to compute, but that part of the change isn't strictly necessary.

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)

I struggled for a while to find a good label for the count parameters, but couldn't find a good convention.

The fundamental problem is that English counting grammar doesn't line up with Swift syntax. In English, you would like to say "prefix of 3 elements", but you can't put a label after a parameter in Swift. Thus, there may not be an idiomatic way to label these parameters. (This is a problem throughout the standard library, actually; I ran into the same issues when I was looking at the UnsafeRawPointer proposal.)

Labels like `ofMaxLength` are verbose, and the use of "length" in particular isn't great when the standard library otherwise uses "count". If you put a gun to my head and demanded I add a label, I would make it `x.removePrefix(ofUpTo: n)` or just `x.removePrefix(of: n)`. But I'm not convinced any of these bring enough to the table. When you see an unlabeled Int parameter to a call with a name like `removePrefix`, there's really only a couple of things it could mean, and the count is an unsurprising one.

On the other hand, the internal parameter name should probably not be `n`; it should be something like `count` or even `countToRemove`.

(Probably obvious, but worth mentioning explicitly: if we add a label, we should apply it consistently to all `prefix` and `suffix` calls which take a count.)

Okay, back to the right order:

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.

No kidding. It requires:

* Two statements, four references to the collection, and 88 characters (for an example with one-letter variable names)
* The use of a complex index-manipulation function
* The simultaneous use of the startIndex, endIndex, and a sign on the count, all of which must be coordinated

I think that, if `removePrefix/Suffix(_:)` were not in the standard library, people would be forced to invent them.

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

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

Does $ represent the start, the end, or either one depending on which side of the range we're on? Because if it's the third option, I think these two operations are actually inverted: the first is removing everything *except* the `prefix(n)`, and the second is removing everything except the `suffix(n)`.

(Or maybe it's based on the sign, with a negative offset going from the endIndex, and a positive offset going from the startIndex? Don't we usually try to avoid that kind of runtime branching?)

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.

Why would it be a good thing to use two different syntaxes for the same operation?

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.

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.

Xcode 8 beta 3-compatible hack based on my assumptions about how this supposed to work, suitable for at least testing the ergonomics: https://gist.github.com/brentdax/3c5c64d3b7ca3ff6b68f1c86163c39c4

At a technical level, I think the biggest problem is the `$` symbol. If it's a constant or variable, then it can't be generic, and we need to fix a specific SignedInteger type for the offset. That means we need to convert to whatever IndexDistance ends up being. That could be fixed if either `$` became a magic syntax or we get generic constants, but either of those will be a significant effort. Or it can be a function, but then it's less convenient.

Stepping back from implementation, though, I think the `$` syntax is just too opaque. It gets better when it's spaced out:

   array[$ + 2 ..< $ - 1]

But it's still not something whose meaning you will even make a wild guess at without looking it up. Even once you learn it, the $ symbol will almost always be squeezed between two other punctuation characters, making visual parsing more difficult. The effect is not unlike stereotypes of Perl.

(Note: I'm an old Perl hand, so others might better read that sentence as "The effect is not unlike Perl.")

I mean, look at this example you gave earlier:

It also implies we can replace

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

with

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

for Collections.

The first version is clear as crystal; the second is clear as mud. The first version gives the parameter a position of prominence; the second buries it in the middle of a complex expression.

Now, abandoning the `$` could help here. I've worked up an alternate design which uses descriptive names at <https://gist.github.com/brentdax/0946a99528f6e6500d93bbf67684c0b3&gt;\. The example I gave above is instead written:

   array[.startIndex + 2 ..< .endIndex - 1]

Or, for the removingPrefix equivalent:

   x[.startIndex + n ..< .endIndex]

I assume start and end index can be used on either end?

Also, since "Index" will already be part of the type name, we can use "start" and "end" here instead.

···

Sent from my iPhone
On Jul 26, 2016, at 5:56 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

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

But this is longer than a removingPrefix call and *still* buries the intent. And adding prefix/suffix operators back in doesn't really help; it merely allows you to omit the straightforward bits, without doing anything to help make the non-straightforward bits more clear. (And in fact, it makes those worse by forcing you to either compress the whitespace in them or parenthesize.)

And even if we get past the syntax issues, there's an attractive nuisance problem. This feature, whatever it is, will undoubtedly circulate as "the way to make String indexes work". Is that something we want to bring to the language?

In short, I think the idea of relative ranges is pretty neat. It's brief, it's parsimonious, it's flexible, it's clever. But it makes code less clear and it hides performance problems. I think either of those two factors by itself ought to make us rethink a Swift proposal.

--
Brent Royal-Gordon
Architechies

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

I second Xiaodi. I am against the slicing subscripts and the ones above look even more unreadable and inscrutable than those in the proposal. I don't understand the rational.

···

On 26 Jul 2016, at 06:50, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

   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.

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.

I don't view it as a sneak. They'd be hard to justify adding without
APIs that use them, so I don't see them coming in as a separate
proposal.

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.

They're as baked as the current language definition makes practical.

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.

Because we don't have important use-cases for those. The ranges that we
do have serve important roles. Just as we wouldn't add incomplete
ranges without APIs to go with them, we also wouldn't add
open-on-the-left ranges if they didn't have an important role to play in
the standard library or common user programs.

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.

Not really; it's a range *expression*, that's intended to be reified
with respect to a collection.

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
    }

Interesting idea, but it seems like it opens a whole can of complexity.
As a consequence, are we talking about supporting a[% 2 == 0] to get the
even-numbered elements of an array?

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

These range expressions are not intended to stand alone.

(I’ve shown all these with reasonable spacing, but IIRC, the odd
no-space-around-range-operators rule is still in effect.

There's no rule, just convention.

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.

Actually not. The API guidelines say that if the thing can function as
a direct object of the verb, you don't put a noun before it. That would
be

    x.remove(somePrefix)

···

on Mon Jul 25 2016, Garth Snyder <swift-evolution@swift.org> wrote:

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

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

--
Dave

Looks like I had missed some messages in the thread. I see the evidence
now.

Sorry,

···

on Tue Jul 26 2016, Anton Zhilin <antonyzhilin-AT-gmail.com> wrote:

I double Nevin on everything he said, especially about incomplete ranges.

2016-07-26 21:13 GMT+03:00 Dave Abrahams via swift-evolution <
swift-evolution@swift.org>:

on Tue Jul 26 2016, Nevin Brackett-Rozinsky < >> nevin.brackettrozinsky-AT-gmail.com> wrote:

> * 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.

FWIW, I don't see any evidence that such a sentiment prevails.

tail-wagging-the-dog?-ly y'rs,

--
Dave

I think we are missing some sort of diff to show the impact of this proposal similar to what we had for the “grand renaming"

···

On Jul 26, 2016, at 1:23 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Tue Jul 26 2016, Anton Zhilin <antonyzhilin-AT-gmail.com <http://antonyzhilin-at-gmail.com/&gt;&gt; wrote:

I double Nevin on everything he said, especially about incomplete ranges.

2016-07-26 21:13 GMT+03:00 Dave Abrahams via swift-evolution <
swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

on Tue Jul 26 2016, Nevin Brackett-Rozinsky < >>> nevin.brackettrozinsky-AT-gmail.com> wrote:

       * 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.

FWIW, I don't see any evidence that such a sentiment prevails.

tail-wagging-the-dog?-ly y'rs,

Looks like I had missed some messages in the thread. I see the evidence
now.

Sorry,

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

The proposal itself shows the impact.

* Full list of renamed methods: <https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md#sequence-end-operations-1&gt;

* New patterns for redesigned `prefix(upTo/through:)` and `suffix(from:)` APIs: <https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md#impact-on-existing-code&gt;

* Source-compatible method/subscript signature changes to work with RangeExpression: <https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md#index-based-operations-1&gt;

Compared to the Great Renaming, this proposal has a tiny footprint:

• 11 methods renamed, with stubs for @available attributes left in extensions on Sequence or Collection
• 3 methods removed (replaced with new subrange syntax)
• 2 types and 6 operators added for incomplete ranges
• 1 protocol added for RangeExpression
• 2 subscripts and 2 methods in 3 protocols changed to work with RangeExpression

I'm working on an implementation (so far I just have `dropFirst` renamed, but I've only spent about half an hour on it, mostly waiting for tests), but the diff isn't really going to tell you much.

···

On Jul 26, 2016, at 3:51 PM, Jose Cheyo Jimenez via swift-evolution <swift-evolution@swift.org> wrote:

I think we are missing some sort of diff to show the impact of this proposal similar to what we had for the “grand renaming"

--
Brent Royal-Gordon
Architechies

* We considered using `first` and `last` as the basis for both
   single-element and multiple-element operations (such that `prefix(3)`
   would become `first(3)`, etc.), but:

   1. These seemed like distinct functionalities, particularly since

      their types are different.

I think the functionality of “Please give me the first 1 element” is
extremely similar to “Please give me the first n elements”.

Keeping those issues in mind, do you still prefer `first(n)` over

`prefix(n)`?

In my subjective opinion, `.first`, `.first(n)`, and `starts(with: [1, 2,
3])` are crystal-clear at the use site, and coexist just fine.
Additionally, `hasPrefix([1, 2, 3])` is equally clear and I would be fine
with that—in particular I am fine with the “get” and “test” methods using
different words.

By contrast, I find `prefix(n)` to be unclear about the role of `n`.
Something like `prefix(limit: n)`

···

On Tue, Jul 26, 2016 at 3:46 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

> On Jul 26, 2016, at 8:30 AM, Nevin Brackett-Rozinsky < > nevin.brackettrozinsky@gmail.com> wrote:
>
> However, I believe that `first(n)` and `last(n)` read more clearly at
the point of use than `prefix(n)` and `suffix(n)`.

I've seen this a couple of times. It's something I bring up in "Other
alternatives":

> * We considered using `first` and `last` as the basis for both
> single-element and multiple-element operations (such that `prefix(3)`
> would become `first(3)`, etc.), but:
>
> 1. These seemed like distinct functionalities, particularly since
> their types are different.
>
> 2. We're not comfortable with heavily overloading a property with a
> bunch of methods, and didn't want to make `first` and `last` into
> methods.
>
> 3. Most APIs work fine, but `hasFirst(_:)` is atrocious, and we see
> no better alternative which includes the word `first`.

To give you an idea of what I mean by #3:

        if numbers.hasFirst([1, 2, 3, 4, 5]) && numbers.hasLast([5, 4, 3,
2, 1]) { … }

Keeping those issues in mind, do you still prefer `first(n)` over
`prefix(n)`?

--
Brent Royal-Gordon
Architechies

the single word “first” only means one element,
but “prefix” can means multiple element.

Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>于2016年7月27日
周三03:46写道:

···

> On Jul 26, 2016, at 8:30 AM, Nevin Brackett-Rozinsky < > nevin.brackettrozinsky@gmail.com> wrote:
>
> However, I believe that `first(n)` and `last(n)` read more clearly at
the point of use than `prefix(n)` and `suffix(n)`.

I've seen this a couple of times. It's something I bring up in "Other
alternatives":

> * We considered using `first` and `last` as the basis for both
> single-element and multiple-element operations (such that `prefix(3)`
> would become `first(3)`, etc.), but:
>
> 1. These seemed like distinct functionalities, particularly since
> their types are different.
>
> 2. We're not comfortable with heavily overloading a property with a
> bunch of methods, and didn't want to make `first` and `last` into
> methods.
>
> 3. Most APIs work fine, but `hasFirst(_:)` is atrocious, and we see
> no better alternative which includes the word `first`.

To give you an idea of what I mean by #3:

        if numbers.hasFirst([1, 2, 3, 4, 5]) && numbers.hasLast([5, 4, 3,
2, 1]) { … }

Keeping those issues in mind, do you still prefer `first(n)` over
`prefix(n)`?

--
Brent Royal-Gordon
Architechies

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

Now finished: [Do not pull] [stdlib] SE-0132 renamings by beccadax · Pull Request #3793 · apple/swift · GitHub

The diff shows a lot of lines touched, but that's mainly unrelated documentation—it turns out an awful lot of examples use `index(of:)`!—and tests.

···

On Jul 26, 2016, at 5:45 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

I'm working on an implementation (so far I just have `dropFirst` renamed, but I've only spent about half an hour on it, mostly waiting for tests), but the diff isn't really going to tell you much.

--
Brent Royal-Gordon
Architechies

   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.

I second Xiaodi. I am against the slicing subscripts and the ones above look even more unreadable and inscrutable than those in the proposal. I don't understand the rational.

The inspiration is D: https://dlang.org/d-array-article.html

···

On Jul 25, 2016, at 11:32 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:
On 26 Jul 2016, at 06:50, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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