I don’t think I am. Maybe I’m confused: the current suggestion is the addition of a $ operator (or labelled subscripts, or another operator) to signify “offset indexing”, yes? As in:
someCollection[$3] == someCollection[someCollection.startIndex.advancedBy(3)] someCollection[$3..<$] == someCollection[someCollection.startIndex.advancedBy(3)..<someCollection.endIndex] I’m not arguing against preserving the indexing of the base array, I understand its benefits. I’m arguing that, instead of using an extra indicator (like $) to indicate offset indexing, with the default being non-offset, why not have the offset indexing be the default, requiring an extra indication (like the label direct) for the non-offset. This would keep the benefits of non-offset indexing, because you’d still have access to it. Is think that’s part of this discussion, right? I could start another thread, if not. Oisín > On 22 Dec 2015, at 20:06, Kevin Ballard <[email protected]> wrote: > > On Mon, Dec 21, 2015, at 08:28 PM, Donnacha Oisín Kidney wrote: >> Why not make the “forgiving” version the default? I mean, the majority of >> python-style composable slicing would be happening on arrays and array >> slices, for which there’s no performance overhead, and the forgiving version >> would seam to suit the “safe-by-default” philosophy. I’ve seen mistakes like >> this: >> >> let ar = [1, 2, 3, 4, 5] >> let arSlice = ar[2..<5] >> arSlice[1] >> >> on a few occasions, for instance. I would think something like this: >> >> let ar = [0, 1, 2, 3, 4, 5] >> >> let arSlice = ar[2...] // [3, 4, 5] >> arSlice[..<3] // [2, 3, 4] >> arSlice[...3] // [2, 3, 4, 5] >> arSlice[direct: 2] // 2 >> arSlice[0] // 2 >> >> Would be what was expected from most programmers learning Swift, while >> leaving the unforgiving option open to those who need it. > > You seem to be arguing against the notion that array slices preserve the > indexing of the base array, but that's not what's under discussion here. > > -Kevin Ballard > >>> On 22 Dec 2015, at 03:29, Dave Abrahams via swift-evolution >>> <[email protected] <mailto:[email protected]>> wrote: >>> >>>> >>>> On Dec 21, 2015, at 1:51 PM, Kevin Ballard <[email protected] >>>> <mailto:[email protected]>> wrote: >>>> >>>> On Mon, Dec 21, 2015, at 11:56 AM, Dave Abrahams wrote: >>>>> >>>>>> On Dec 19, 2015, at 8:52 PM, Kevin Ballard via swift-evolution >>>>>> <[email protected] <mailto:[email protected]>> wrote: >>>>>> >>>>>> On Fri, Dec 18, 2015, at 02:39 PM, Dave Abrahams via swift-evolution >>>>>> wrote: >>>>>>> >>>>>>> Yes, we already have facilities to do most of what Python can do here, >>>>>>> but one major problem IMO is that the “language” of slicing is so >>>>>>> non-uniform: we have [a..<b], dropFirst, dropLast, prefix, and suffix. >>>>>>> Introducing “$” for this purpose could make it all hang together and >>>>>>> also eliminate the “why does it have to be so hard to look at the 2nd >>>>>>> character of a string?!” problem. That is, use the identifier “$” >>>>>>> (yes, that’s an identifier in Swift) to denote the beginning-or-end of >>>>>>> a collection. Thus, >>>>>>> >>>>>>> c[c.startIndex.advancedBy(3)] =>c[$+3] // Python: c[3] >>>>>>> c[c.endIndex.advancedBy(-3)] =>c[$-3] // Python: c[-3] >>>>>>> >>>>>>> c.dropFirst(3) =>c[$+3...] // Python: c[3:] >>>>>>> c.dropLast(3) =>c[..<$-3] // Python: c[:-3] >>>>>>> c.prefix(3) =>c[..<$+3] // Python: c[:3] >>>>>>> c.suffix(3) => c[$-3...] // Python: c[-3:] >>>>>>> >>>>>>> It even has the nice connotation that, “this might be a little more >>>>>>> expen$ive than plain indexing” (which it might, for non-random-access >>>>>>> collections). I think the syntax is still a bit heavy, not least >>>>>>> because of “..<“ and “...”, but the direction has potential. >>>>>>> >>>>>>> I haven’t had the time to really experiment with a design like this; >>>>>>> the community might be able to help by prototyping and using some >>>>>>> alternatives. You can do all of this outside the standard library with >>>>>>> extensions. >>>>>> >>>>>> Interesting idea. >>>>>> >>>>>> One downside is it masks potentially O(N) operations >>>>>> (ForwardIndex.advancedBy()) behind the + operator, which is typically >>>>>> assumed to be an O(1) operation. >>>>> >>>>> Yeah, but the “$” is sufficiently unusual that it doesn’t bother me too >>>>> much. >>>>> >>>>>> Alos, the $+3 syntax suggests that it requires there to be at least 3 >>>>>> elements in the sequence, but prefix()/suffix()/dropFirst/etc. all take >>>>>> maximum counts, so they operate on sequences of fewer elements. >>>>> >>>>> For indexing, $+3 would make that requirement. For slicing, it wouldn’t. >>>>> I’m not sure why you say something about thesyntaxsuggests exceeding >>>>> bounds would be an error. >>>> >>>> Because there's no precedent for + behaving like a saturating addition, >>>> not in Swift and not, to my knowledge, anywhere else either. The closest >>>> example that comes to mind is floating-point numbers eventually ending up >>>> at Infinity, but that's not really saturating addition, that's just a >>>> consequence of Infinity + anything == Infinity. Nor do I think we should >>>> be establishing precedent of using + for saturating addition, because that >>>> would be surprising to people. >>> >>> To call this “saturating addition” is an…interesting…interpretation. I >>> don’t view it that way at all. The “saturation,” if there is any, happens >>> as part of subscripting. You don’t even know what the “saturation limit” >>> is until you couple the range expression with the collection. >>> >>> In my view, the addition is part of an EDSL that represents a notional >>> position offset from the start or end, then the subscript operation >>> forgivingly trims these offsets as needed. >>> >>>> Additionally, I don't think adding a $ to an array slice expression should >>>> result in a behavioral difference, e.g. array[3..<array.endIndex] and >>>> array[$+3..<$] should behave the same >>> >>> I see your point, but don’t (necessarily) agree with you there. “$” here >>> is used as an indicator of several of things, including >>> not-necessarily-O(1) and forgiving slicing. We could introduce a label >>> just to handle that: >>> >>> array[forgivingAndNotO1: $+3..<$] >>> >>> but it doesn’t look like a win to me. >>> >>>> >>>>>> There's also some confusion with using $ for both start and end. What if >>>>>> I say c[$..<$]? We'd have to infer from position that the first $ is the >>>>>> start and the second $ is the end, but then what about c[$+n..<$+m]? We >>>>>> can't treat the usage of + as meaning "from start" because the argument >>>>>> might be negative. And if we use the overall sign of the >>>>>> operation/argument together, then the expression `$+n` could mean from >>>>>> start or from end, which comes right back to the problem with Python >>>>>> syntax. >>>>> >>>>> There’s a problem with Python syntax? I’m guessing you mean that c[a:b] >>>>> can have very different interpretations depending on whether a and b are >>>>> positive or negative? >>>> >>>> Exactly. >>>> >>>>> First of all, I should say: that doesn’t really bother me. The 99.9% use >>>>> case for this operation uses literal constants for the offsets, and I >>>>> haven’t heard of it causing confusion for Python programmers. That said, >>>>> if we wanted to address it, we could easily require n and m above to be >>>>> literals, rather than Ints (which incidentally guarantees it’s an O(1) >>>>> operation). That has upsides and downsides of course. >>>> >>>> I don't think we should add this feature in any form if it only supports >>>> literals. >>>> >>>>>> I think Jacob's idea has some promise though: >>>>>> >>>>>> c[c.startIndex.advancedBy(3)] => c[fromStart: 3] >>>>>> c[c.endIndex.advancedBy(-3)] => c[fromEnd: 3] >>>>> >>>>>> But naming the slice operations is a little trickier. We could actually >>>>>> just go ahead and re-use the existing method names for those: >>>>>> >>>>>> c.dropFirst(3) => c[dropFirst: 3] >>>>>> c.dropLast(3) => c[dropLast: 3] >>>>>> c.prefix(3) => c[prefix: 3] >>>>>> c.suffix(3) => c[suffix: 3] >>>>>> >>>>>> That's not so compelling, since we already have the methods, but I >>>>>> suppose it makes sense if you want to try and make all slice-producing >>>>>> methods use subscript syntax (which I have mixed feelings about). >>>>> >>>>> Once we get efficient in-place slice mutation (via slice addressors), it >>>>> becomes a lot more compelling, IMO. But I still don’t find the naming >>>>> terribly clear, and I don’t love that one needs to combine two subscript >>>>> operations in order to drop the first and last element or take just >>>>> elements 3..<5. >>>> >>>> You can always add more overloads, such as >>>> >>>> c[dropFirst: 3, dropLast: 5] >>>> >>>> but I admit that there's a bunch of combinations here that would need to >>>> be added. >>>> >>> >>> My point is that we have an English language soup that doesn’t compose >>> naturally. Slicing in Python is much more elegant and composes well. If >>> we didn’t currently have 6 separate methods (7 including subscript for >>> index-based slicing) for handling this, that need to be separately >>> documented and understood, I wouldn’t be so eager to replace the words with >>> an EDSL, but in this case IMO it is an overall simplification. >>> >>>> My concern over trying to make it easier to take elements 3..<5 is that >>>> incrementing indexes is verbose for a reason, and adding a feature that >>>> makes it really easy to index into any collection by using integers is a >>>> bad idea as it will hide O(N) operations behind code that looks like O(1). >>>> And hiding these operations makes it really easy to accidentally turn an >>>> O(N) algorithm into an O(N^2) algorithm. >>> >>> As I’ve said, I consider the presence of “$” to be enough of an indicator >>> that something co$tly is happening, though I’m open to other ways of >>> indicating it. I’m trying to strike a balance between “rigorous” and “easy >>> to use,” here. Remember that Swift has to work in playgrounds and for >>> beginning programmers, too. I am likewise unsatisfied with the (lack of) >>> ease-of-use of String as well (e.g. for lexing and parsing tasks), and have >>> made improving it a priority for Swift 3. I view fixing the slicing >>> interface as part of that job. >>> >>>>> Even if we need separate symbols for “start” and “end” (e.g. using “$” >>>>> for both might just be too confusing for people in the end, even if it >>>>> works otherwise), I still think a generalized form that allows ranges to >>>>> be used everywhere for slicing is going to be much easier to understand >>>>> than this hodgepodge of words we use today. >>>> >>>> I'm tempted to say that if we do this, we should use two different sigils, >>>> and more importantly we should not use + and - but instead use methods on >>>> the sigils like advancedBy(), as if the sigils were literally placeholders >>>> for the start/end index. That way we won't write code that looks O(1) when >>>> it's not. For example: >>>> >>>> col[^.advancedBy(3)..<$] >>>> >>>> Although we'd need to revisit the names a little, because $.advancedBy(-3) >>>> is a bit odd when we know that $ can't ever take a non-negative number for >>>> that. >>>> >>>> Or maybe we should just use $ instead as a token that means "the >>>> collection being indexed", so you'd actually say something like >>>> >>>> col[$.startIndex.advancedBy(3)..<$.startIndex.advancedBy(5)] >>> >>> I really like that direction, but I don’t think it does enough to solve the >>> ease-of-use problem; I still think the result looks and feels horrible >>> compared to Python for the constituencies mentioned above. >>> >>> I briefly implemented this syntax, that was intended to suggest repeated >>> incrementation: >>> >>> col.startIndex++3 // col.startIndex.advancedBy(3) >>> >>> I don’t think that is viable, especially now that we’ve dropped “++” and >>> “--“. But this syntax >>> >>> col[$.start⛄️3..<$.start⛄️5] >>> >>> begins to be interesting for some definition of ⛄️. >>> >>>> This solves the problem of subscripting a collection without having to >>>> store it in a local variable, without discarding any of the intentional >>>> index overhead. Of course, if the goal is to make index operations more >>>> concise this doesn't really help much, but my argument here is that it's >>>> hard to cut down on the verbosity without hiding O(N) operations. >>> >>> That ship has already sailed somewhat, because e.g. every Collection has to >>> have a count property, which can be O(N). But I still like to uphold it >>> where possible. I just don’t think the combination of “+” and “$” >>> necessarily has such a strong O(1) connotation… especially because the >>> precedent for seeing those symbols together is regexps. >>> >>>> >>>> -Kevin Ballard >>>> >>>>>> But the [fromStart:] and [fromEnd:] subscripts seem useful. >>>>> Yeah… I really want a unified solution that covers slicing as well as >>>>> offset indexing. >>>>> >>>>> -Dave >>>>> >>>> >>> >>> >>> -Dave >>> >>> >>> >>> _______________________________________________ >>> swift-evolution mailing list >>> [email protected] <mailto:[email protected]> >>> https://lists.swift.org/mailman/listinfo/swift-evolution >>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
