A do agree with a lot of others, I don't think the ++
and --
syntax is a good fit. ++
or --
along with the range syntax (...
or ..<
) and the square brackets for collections is a lot of non-alphanumeric characters to look at and I find it hard to read.
I like where @gregtitus went with the NSLayoutConstraint suggestion, but I think we could get it down to one character.
If we take the pipe character as the edge to offset from (a collection bound or index) we can create we end up with the patterns: |#
and #|
where "#" is an index. |#
would mean to offset by "#" to the right, and #|
means offset # to the left. If we want to offset from an index like String.Index
, we can add the variable containing the index before or after the pipe. So index|#
would mean to offset by "#" to the right, and #|index
would mean by offset by "#" to the left.
A basic example using only numbers would look like:
let str = "abcdefghijklmnopqrstuvwxyz"
print(str[14|]) // m
print(str[|5]) // f
If have an index we want to offset from:
let str = "abcdefghijklmnopqrstuvwxyz"
let idx = str.firstIndex { $0 == "n" }!
print(str[1|idx]) // m
print(str[idx|1]) // o
If we want to grab a range of elements:
let str = "abcdefghijklmnopqrstuvwxyz"
let idx = str.firstIndex { $0 == "n" }!
print(str[|20..<2|]) // uvwx
print(str[1|idx...idx|1]) // mno
Using @Michael_Ilseman 's examples:
let str = "abcdefghijklmnopqrstuvwxyz"
let idx = str.firstIndex { $0 == "n" }!
// -- single element subscript --
print(str[14|]) // m
print(str[100|idx]) // a
print(str[idx|1]) // o
print(str[10|(idx|1)]) // e
// -- relative range --
print(str[|1 ..< 2|]) // bcdefghijklmnopqrstuvwx
print(str[2|idx ..< 2|]) // lmnopqrstuvwx
print(str[idx ..< 2|]) // nopqrstuvwx
print(str[2|idx..<idx]) // lm
print(str[2|idx..<idx|3]) // lmnop
print(str[4| ..< 2|]) // wx
// -- relative range through --
print(str[2|idx ... 2|]) // lmnopqrstuvwxy
print(str[idx ... 2|]) // nopqrstuvwxy
print(str[2|idx...idx]) // lmn
print(str[2|idx...idx|3]) // lmnopq
print(str[4| ... 2|]) // wxy
// -- partial relative range up to --
print(str[..<idx|2]) // abcdefghijklmno
print(str[..<2|idx]) // abcdefghijk
print(str[..<|20]) // abcdefghijklmnopqrst
print(str[..<20|]) // abcdef
// -- partial relative range through --
print(str[...idx|2]) // abcdefghijklmnop
print(str[...2|idx]) // abcdefghijkl
print(str[...|20]) // abcdefghijklmnopqrstu
print(str[...20|]) // abcdefg
// -- partial relative range from --
print(str[idx|2...]) // pqrstuvwxyz
print(str[2|idx...]) // lmnopqrstuvwxyz
print(str[|20...]) // uvwxyz
print(str[20|...]) // ghijklmnopqrstuvwxyz
The only issue that I can find with this is that when we have an collection with an integer based index, and we want to offset from said index, the index and direction of offset will be ambiguous.
What does this mean?
let arr = ["a", "b", "c", "d", "e", "f", "g"]
let idx = arr.firstIndex(of: "d")
print(arr[idx|3]) // Is this "g" or "a"?
The only way this could be solved is by either:
- Have every collection index work the same way
String.Index
does. That way there is always oneInt
and oneIndex
type. - Provide an additional alternate syntax for collections with integer indices where the relative bound is ambiguous. Perhaps using the NSLayoutConstraint syntax:
|-
and-|
where the "-" is on the side of the offset, or|:
and:|
.
I would like to see this proposal as a part of Swift, I just think that ++
and --
aren't the best option and would like to see a single character option for most use cases.