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