Shorthand for Offsetting startIndex and endIndex

IMO we should be thinking in terms of replacing many SubSequence-returning operations with something equally legible. I don't want to keep a.prefix(3), a.dropFirst() and a.dropLast(10) around (at least, not without deprecation) once we have a unified slicing syntax that handles these cases. Most of the proposals I've seen don't seem to result in a readability win over a.prefix(3).

All of these:

let m1 = s[...s.index(s.startIndex, offsetBy: 4)]
let m2 = s[offset: ...(s.startIndex + 4)]
let m3 = s[...s.startIndex.offset(by: 4)]

are just bad ways to write:

let m4 = s.prefix(5)

You can also combine these

let m4 = s.dropFirst(5).prefix(5) // elements 5...9

The only functional reason to have a slicing syntax for this is so that we can do mutations:

 s[...s.startIndex.offset(by: 4)].sort() // sort the first 5 elements

I'm not saying I have the answer of course: every suggestion I've made in the past has raised vociferous objections from some people. For example,

 s[..<++5].sort()         // sort the first 5 elements
 s[--5...].sort()         // sort the last 5 elements
 let tail = s[..<++1]     // let tail = s.dropFirst()

It also composes; you're not stuck with offsets on both ends (a very minor win):

 s[someIndex..<--42].sort() // sort elements starting at someIndex, 
                            // ending 42 before endIndex

I think the main problem most people have is that it's only legible if you know the notation, though they usually pronounce that concern as something more like “^%@!&%!, Dave!”

That might be a slight overreaction: we're already in "interesting notation" territory with a[i..<j] and I for one am happy to embrace that fully…but I guess others feel differently.

Other possible directions, none of which I find very satisfying:

  • Extend the language to allow prefix et. al to become “lvalue-returning functions” so you could write

    x.prefix(3).sort()
    
  • Add variants of the prefix/suffix/drop methods to support mutation, such as

    x.mutatingPrefix(3) { $0.sort() }
    
  • Give up on mutation and just keep using the prefix/suffix/drop methods

My main complaint about all of these is that they all either maintain the size of, or expand, an API that is IMO already too large and that fails to draw all these similar operations together syntactically.

7 Likes