> On Jul 1, 2016, at 3:50 PM, Dave Abrahams via swift-evolution > <swift-evolution@swift.org> wrote: >> • 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.
I'll try to figure out how to wedge something into stdlib, but until I do, here's something I tried out in a playground: https://gist.github.com/brentdax/b36ef130873b752d4c6f7ee3c157d07d (Already sent that to you, Dave; this is for everyone else.) >> • 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. [and] >> first 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 I was hoping to distinguish between the O(1), always-anchored first/last calls and the O(n), unanchored earliest/latest calls. Perhaps that isn't necessary, though; `xs.first(x)` *does* read well, and it'd be difficult to imagine an implementation on most collections that didn't involve searching multiple elements. >> 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) "Operand" is meant to label the column below it, which lists things like "First 1 Element". Maybe I should just leave that cell blank, though. >> The term-of-art exception is not a suicide pact; > > Tatoo that on your forehead, mister! Touché. (I still believe map, reduce, and filter are much stronger terms of art and don't require much modification, but that's a discussion for another thread.) >> The 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. Maybe. I suspect most users simply use the index without manipulating it, but certainly you'll sometimes use both. (But even without that, I still think the directional vagueness and the possibility of a `lastIndex` method in the future are good enough justifications to rename it on their own.) >> 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? I don't like changing the base name—it breaks the connection between `prefix(_:)` and `prefix(while:)`—unless we change it for all of the relevant calls, but I'm certainly open to changing the label. Maybe `prefix(whereEach: isFull)`? On the other hand, does `xs.prefix(3)` read well? APIs that take counts seem to be challenging to name; I've had some of the same problems with UnsafeRawPointer APIs. > [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] I'll try to find a way to fit examples in. >> Preferred (ambitious) option >> >> let head = people[..<i] >> let tail = people[i..<] > > let equivalentTail = people[i...] // reads a bit better, no? > let headThroughI = people[...i] It looks nicer, but it breaks the mental model of these unary forms merely filling in `startIndex` or `endIndex` automatically. Like the `..<` operator itself, I think we're better off with ugly clarity than pretty vagueness. -- Brent Royal-Gordon Architechies _______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution