The incomplete range concept is quite intriguing. Have we considered spelling the operators with an asterisk at the incomplete end? prefix *..< prefix *... postfix ...* postfix ..<*
That way the use-sites would look like: someCollection[*..<idx] someCollection[*...idx] someCollection[idx...*] someCollection[idx..<*] >From a “first-glance” perspective, the asterisk “looks like” a wildcard placeholder, which should help readers and writers of code to understand the meaning. And from a future language development standpoint, we’ll keep the triple-dot spelling available for whatever needs may arise (tuple splatting, variadic generics, etc.) Thoughts? Nevin On Fri, Jul 1, 2016 at 6:50 PM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote: > > on Thu Jun 23 2016, Brent Royal-Gordon <swift-evolution@swift.org> wrote: > > > As previously threatened mentioned, I've written a draft proposal to > > fix a number of naming issues with APIs operating on the beginning and > > end of Sequences and Collections: > > > > • Inconsistent use of `prefix`/`suffix` vs. `first`/`last` > > • Confusing naming of `drop` methods > > • Ambiguous naming of `index(of:/where:)` and `drop(while:)` > > • `prefix(upTo:)`, `prefix(through:)`, and `suffix(from:)` shouldn't > > be part of this family at all > > > > To fix this, I propose: > > > > • Renaming all methods which operate on more than one element at the > > beginning/end to use "prefix" or "suffix", not "first" or "last" > > • Renaming `index(of:/where:)` to `earliestIndex(…)` and > > `first(where:)` to `earliest(where:)` > > What's wrong with firstIndex(of:/where:) [and lastIndex(of:/where:)]? > That seems like a much less esoteric way to phrase it that meshes well > with the meanings of > > xs.first > xs.indices.first > > etc. > > > • Renaming the `drop` methods to use `removing` > > Very clever! I *like*. > > > • Redesigning `prefix(upTo:)`, `prefix(through:)` and `suffix(from:)` > > as subscripts with "partial" ranges, like `people[..<idx]` or perhaps > > `people[nil..<idx]`. > > Yes please; I really want this. This part is a slightly nontrivial > design problem, though. Someone should build an implementation before > the actual design is proposed. Probably the best way would be to > leave prefix and suffix alone for the moment and add/test the new > subscripts. > > > Since that last point requires significant redesign, including the > > introduction of new types, I have also included an alternative design > > which uses `people[to: idx]` instead. > > I really don't like using labels for this, because stride(to:) and > stride(through:) have already spawned a naming bikeshed with no clear > resolution, suggesting that no name works. Plus, the ..< operator > already implies the name. > > > This proposal does not seek to add new functionality; it merely > > renames or (in the case of the "aggressive" subscript option) > > redesigns existing functionality. I do, however, discuss (without > > making many judgements about their wisdom) how these changes might > > affect the naming of functionality we might add in future versions of > > Swift. > > Good. > > > I would mainly like feedback on the two most open questions left in > > this proposal: > > > > • The choice of `removing` to replace `drop` > > It's 100% appropriate, provided that the APIs match some corresponding > mutating remove API. Nonmutating operations are often implemented via > lazy adaptors... which a slice can be viewed to be. So I think this is > a beautiful answer. > > > • The decision about whether to use `people[..<idx]`, > > `people[nil..<idx]`, or `people[to: idx]`. > > I prefer how the first one reads. > > > But I'd also like comments on the rest of the proposal, and on whether > > I should split the prefix(upTo:/through:)/suffix(from:) changes into a > > separate proposal from the rest. > > I very much appreciate that you're addressing all of these at once. > > > I suspect this will cause a firestorm of bikeshedding, so please try > > to keep your suggestions grounded. Don't just suggest a name; > > articulate why it's a better choice than what we already have or what > > this proposal suggests. Only you can prevent our first > > *three*-hundred-message bikeshedding thread. > > > > Thanks for your attention! > > > > (P.S. The proposal below includes several huge tables which may cause > > some mail clients to become very pouty and refuse to eat their > > supper. You may want to read the proposal at > > <https://gist.github.com/brentdax/024d26c2b68b88323989540c06261430 > > <https://gist.github.com/brentdax/024d26c2b68b88323989540c06261430>> > > instead.) > > > > The Sequence and Collection protocols offer a wide variety of APIs which > > are defined to operate on, or from, one end of the sequence: > > > > Operand Get Index Exclude Remove (1) Pop (1) Equate (2) > > I think you want “Operation” or “Semantics” rather than “Operand” (which > means an argument to an operation) > > > Fixed Size > > First 1 C.first - S.dropFirst() C.removeFirst() C.popFirst() - > > Last 1 C.last - S.dropLast() C.removeLast() C.popLast() - > > First (n: Int) S.prefix(_:) - S.dropFirst(_:) C.removeFirst(_:) - > S.starts(with:) > > ...with closure S.prefix(while:) - S.drop(while:) - - S.starts > > (with:isEquivalent:) > > Last (n: Int) S.suffix(_:) - S.dropLast(_:) C.removeLast(_:) - - > > ...with closure - - - - - - > > Searching From End > > First matching - C.index(of:) - - - - > > element > > ...with closure S.first(where:) C.index(where:) - - - - > > Last matching element - - - - - - > > ...with closure - - - - - - > > Based on Index > > startIndex ..< (i: Index) C.prefix(upTo:) - - - - - > > startIndex ... (i: Index) C.prefix(through:) - - - - - > > (i: Index) ..< endIndex C.suffix(from:) - - - - - > > > > I have included several blank rows for operands which fit the APIs' > patterns, even if they don't happen to have any operations currently. > > > > Type abbreviations: > > > > * S = Sequence > > * C = Collection (or a sub-protocol like BidirectionalCollection) > > > > Notes: > > > > 1 remove and pop both mutate the array to delete the indicated > element(s), but remove assumes as a precondition that the indicated > elements exist, while pop > > checks whether or not they exist. > > > > 2 String and NSString have bespoke versions of first n and last n > Equate operations, in the form of their hasPrefix and hasSuffix methods. > > > > Leaving aside the question of whether any gaps in these tables ought to > be filled, I see a number of issues with existing terminology. > > > > SVG ImageInconsistent use of prefix and suffix > > > > Some APIs which operate on a variable number of elements anchored at one > end or the other use the terms prefix or suffix: > > > > * Sequence.prefix(_:) and Sequence.suffix(_:) > > * Sequence.prefix(while:) > > * String.hasPrefix(_:) and String.hasSuffix(_:) > > > > Others, however, use first or last: > > > > * Sequence.dropFirst(_:) and Sequence.dropLast(_:) > > * Sequence.removeFirst(_:) and Sequence.removeLast(_:) > > > > Still others use neither: > > > > * Sequence.starts(with:) > > * Sequence.drop(while:) > > > > These methods are all closely related, but because of this inconsistent > terminology, they fail to form predictable method families. > > > > SVG Imagefirst has multiple meanings > > > > The word first can mean three different things in these APIs: > > > > * Just the very first element of the sequence. > > > > * A subsequence of elements anchored at the beginning of the sequence, > > as mentioned in the last point. > > > > * The first element encountered in the sequence which matches a given > > criterion when walking from the beginning of the sequence towards the > > end. > > > > It would be nice to have more clarity here. > > You seem to be suggesting that a word needs to mean exactly the same > thing regardless of context. If so, I disagree. If I say “the first > element” or “the first element greater than 5” there's absolutely no > lack of clarity AFAICT. That accounts for the first and last bullets > > The usage in the middle bullet is open to misinterpretation and I would > support fixing that. > > xs.removeFirst(42) > > could read like, “remove the first element equal to 42.” > > > SVG Imagedrop is misleading and scary > > > > In a Swift context, I believe the drop methods are actively confusing: > > > > * drop does not have the -ing or -ed suffix normally used for a > > nonmutating method. > > > > * drop has strong associations with destructive operations; it's the > > term used, for instance, for deleting whole tables in SQL. Even > > dropping would probably sound more like a mutating operation than > > alternatives. > > > > * As previously mentioned, the use of dropFirst and dropLast for > > single-drop operations and multiple-drop operations breaks up method > > families. > > > > drop, dropFirst, and dropLast are terms of art, so we allow them a > > certain amount of leeway. However, I believe the drop functions go > > well beyond what we should > > permit. They are relatively uncommon operations, associated primarily > > with functional languages rather than mainstream object-oriented or > > imperative languages, and > > their violation of the normal Swift naming guidelines is especially > > misleading. > > > > The term-of-art exception is not a suicide pact; > > Tatoo that on your forehead, mister! > > > it is meant to aid understanding by importing common terminology, not > > bind us to follow every decision made by any language that came before > > us. In this case, I think we should ignore precedent and forge our own > > path. > > > > SVG ImageUnstated direction of operation > > > > Several APIs could theoretically be implemented by working from either > > end of the sequence, and would return different results depending on > > the direction, but do not indicate the direction in their names: > > > > * Sequence.drop(while:) > > * Collection.index(of:) > > > > Adding a direction to these APIs would make their behavior clearer and > permit us to offer opposite-end equivalents in the future. (Unmerged > swift-evolution pull > > request 329 would add lastIndex methods.) > > > > SVG ImageThe index(...) base name has been polluted > > > > Swift 3's new collection model placed a number of low-level index > > manipulating operations on the base method name index. These now share > > that name with index(of:) and index(where:), which are much > > higher-level operations. This may be confusing for users looking for > > high-level operations; the only real relationship between the two sets > > of operations is that they both return an index. > > There's another relationship. Once you call the high-level operation, > you're now in the domain of indexing, and are very likely to ask for the > index(after:) the one you found. > > > It would be nice to separate these two groups of methods into > > different families. > > I used to think that was important, but I no longer do given the above. > > > SVG ImageOperations taking an index are really slicing > > > > prefix(upTo:), prefix(through:), and suffix(from:) at first appear to > > belong to the same family as the other prefix and suffix methods, but > > deeper examination reveals otherwise. They are the only operations > > which take indices, and they don't cleanly extend to the other > > operations which belong to these families. (For instance, it would not > > make sense to add a dropPrefix(upTo:) method; it would be equivalent > > to suffix(from:).) > > > > Also, on Int-indexed collections like Array, prefix(_:) and > > prefix(upTo:) are identical, but there is little relationship between > > suffix(_:) and suffix(from:), which is confusing. > > > > suffix(from:) is a particularly severe source of confusion. The other > > suffix APIs all have parameters relative to the endof the collection, > > but suffix(from:)'s index is still relative to the beginning of the > > array. This is obvious if you think deeply about the meaning of an > > index, but we don't really want to force our users to stare at a > > strange API until they have an epiphany. > > > > I believe these operations have much more in common with slicing a > > collection using a range, and that reimagining them as slicing APIs > > will be more fruitful. > > Yes please. > > > SVG ImageWhy does it matter? > > > > Many of these APIs are only occasionally necessary, so it's important > > that they be easy to find when needed and easy to understand when > > read. If you know that prefix (10) will get the first ten elements but > > don't know what its inverse is, you will probably not guess that it's > > dropFirst(10). The confusing, conflicting names in these APIs are a > > barrier to users adopting them where appropriate. > > > > SVG ImageProposed solution > > > > We sever the index-taking APIs from the others, forming two separate > > families, which I will call the "Sequence-end operations" and the > > "index-based operations". We then consider and redesign them along > > separate lines. > > > > SVG ImageSequence-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 earliest > > ...with closure earliest > > Last matching element latest > > ...with closure latest > > > > To accomplish this, starts(with:) should be renamed to hasPrefix(_:), > > +1 > > > > > 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), I think they are > > similar enough to deserve to be treated as nonmutating forms. > > > > These changes yield (altered names bold): > > > > Operand 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(_:) - S.removingPrefix(_:) C.removePrefix(_:) - > S.hasPrefix(_:) > > ...with closure S.prefix(while:) - S.removingPrefix - - S.hasPrefix > > (while:) (_:isEquivalent:) > > Call me overly fussy, but I don't love the use of “while” here because > it seems stateful. > > xs.prefix(while: isFull) > > That reads like I'm going to repeatedly take the prefix of xs while some > isFull property is true. The most descriptive usage I can think of is > > for x in xs.longestPrefix(where: isFull) > > What do you think? > > [BTW, you might need to stop using a table because it's already too > wide, but your examples *really* ought to be showing use cases rather > than signatures, c.f. the table in > https://github.com/apple/swift/pull/2981. Otherwise it's hard] > > > Last (n: Int) S.suffix(_:) - S.removingSuffix(_:) C.removeSuffix(_:) - > > - ...with closure - - - - - - Searching From End First matching - > > C.earliestIndex(of:) - - - - element ...with closure > > S.earliest(where:) C.earliestIndex - - - - (where:) Last matching > > element - - - - - - ...with closure - - - - - - > > > > SVG ImageAlternative to removing > > > > If the type differences are seen as disqualifying removing as a > > replacement for drop, > > They are not! > > > I suggest using skipping instead. > > > > There are, of course, many possible alternatives to skipping; this is > > almost a perfect subject for bikeshedding. I've chosen skipping > > because: > > > > 1 It is not an uncommon word, unlike (say) omitting. This means > > non-native English speakers and schoolchildren are more likely to > > recognize it. > > > > 2 It is an -ing verb, unlike (say) without. This makes it fit common > > Swift naming patterns more closely. > > > > 3 It does not imply danger, unlike (say) dropping, nor some sort of > > ongoing process, unlike (say) ignoring. This makes its behavior more > > obvious. > > > > If you want to suggest an alternative on swift-evolution, please do > > not merely mention a synonym; rather, explain why it is an improvement > > on either these axes or other ones. (I would be particularly > > interested in names other than removing which draw an analogy to > > something else in Swift.) > > > > SVG ImageIndex-based operations > > > > Because these APIs look up elements based on their indices, I believe > these operations should be exposed as subscripts, and ideally should look > like other slicing > > operations. > > > > My primary design is rather ambitious, introducing two new types and > either two operator overloads, or four unary forms of existing binary > operators. I therefore > > present a more conservative alternative as well. > > > > SVG ImagePreferred (ambitious) option > > > > let head = people[..<i] > > let tail = people[i..<] > > let equivalentTail = people[i...] // reads a bit better, no? > let headThroughI = people[...i] > > > let rearrangedPeople = tail + head > > > > Or this small variation: > > > > let head = people[nil ..< i] > > let tail = people[i ..< nil] > > let rearrangedPeople = tail + head > > > > The operators would construct instances of a new pair of types, > > IncompleteRange (for ..<) and IncompleteClosedRange (for ...), and > > Collection would include new subscripts taking these types. These > > would probably have default implementations which constructed an > > equivalent Range or ClosedRange using startIndex and endIndex, then > > passed the resulting range through to the existing subscripts. > > W00t! > > > > > I prefer this option because it offers an elegant syntax immediately > > recognizable as a form of slicing, and provides a straightforward way > > for a future version of Swift to extend other Range-handling > > Collection operations, like replaceSubrange(_:with:) and > > removeSubrange(_:), to handle subranges bound by the ends of the > > Collection. > > -- > Dave > > _______________________________________________ > 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