This isn’t much more than a convenience feature, to make it quicker and more compact to state an integer value one greater than or less than another integer value.
Motivation
someInt + 1 and someInt - 1 are pretty common expressions, but they’re not very compact or easy to type, even without the parentheses they often have to use in larger expressions. That’s up to six extra symbols (including three shift key presses) just to state a value one different from another.
Detailed design
The behavior of this feature can be specified exactly by the following:
postfix operator +
postfix operator -
postfix func + (n: Int) -> Int {
return n + 1
}
postfix func - (n: Int) -> Int {
return n - 1
}
As postfix operators, they would take precedence over all binary operators, allowing one to avoid parentheses in larger expressions. For example, the expression 1+ * 2 == 2 * 1+ == (1 + 1) * 2 would still evaluate to 4. Likewise, 1- * 2 == (1 - 1) * 2 would evaluate to 0.
As an admittedly superfluous example, for an array v, one could refer to its final element using v[v.count-].
Please note, these operators are not intended to mimic the behavior of increment and decrement operators in other languages (++ and --). No values would be mutated by the use of these operators. They would simply return the result of adding or subtracting one from an integer, without changing the value of the operand.
Is there precedent in mathematics or computer science for these operators? It is not obvious (to me) at a glance what they do, but relevant prior art would help you build a case for introducing them to the language.
Edit: Wikipedia's page on this subject can be found here.
I’m not aware of any other case where this functionality is fulfilled in this form. Haskell has “succ” and “pred”, but those would defeat the idea of compactness. The hope is that prior awareness of increment and decrement operators would allow one to take a guess at what these operators do.
Is this needed though? Imo a bare postfix + introduces a lot more ambiguity when used anywhere other than indexing - I would not personally expect 1+ * 2 to produce 4, nor 1- * 2 to produce 0. The space savings aren't that immense either, just one character from myInt-1 to myInt-
However, I do understand the need for accessing final elements or fixing off-by-one errors. I usually do one of two things:
Define my own lastIndex property on Collection which just gives the last valid index, similar to count-1.
Use count-1 without any whitespaces for the -, which make its purpose as a singular offset quite clear
The utility of this seems only to subvert order of operations. There are already several ways to do this. Parentheses are the best; take out the surrounding spaces if you like that.
While there have been some notable exceptions, in general, language changes which only locally eliminate a constant number of tokens at a call site are unlikely to gain traction, especially when the alternative (writing x + 1 and x - 1) is so concise to begin with.
Compare this with, eg, generics, which can eliminate large swathes of code duplication when used properly, or data race safety, which eliminates a large number of bugs in one fell swoop.
I’m not a fan of this particular proposal as it currently stands, and of course the importance of this particular problem is orders of magnitude lower than those solved by generics or strict concurrency.
But I do think that in discussions like this we should acknowledge that the number of characters is a misleading metric for determining how difficult something is to write. Auto-complete, especially when it intelligently sorts the completions based on the static typing of the context, can mean that a 50-character member can be significantly easier to type than (x + 1). Sometimes typing the member is as simple as . followed by return, whereas using an operator suddenly means I have jump back several tokens to add the leading (, I have to combine shift with other keys several times, and formatting is worsened, e.g.:
I never realised map could be used on a single integer... to be honest I don't understand what would that do.. and if there were explicit function like incremented or toggled I'd definitely prefer that (in one of my code bases I was using a postfix Borat inspired .not operation).
In the particular example above I'd simply write:
let result = selectedUser
.profile
.personalInfo
.age + 1
however, if the chain were to continue, then indeed a postfix operation would be cool to have:
let result = selectedUser
.profile
.personalInfo
.age
.adding(1)
.somethingElse()
We have a similar (but not quite the same) thing already:
...
.age
.addingReportingOverflow(1).partialValue // overflow is ignored :-(
.somethingElse()
I can see the case for members over separate operators, since an operator would certainly be materially worse in method chains.
Really my whole point here is that when I see expressions like (i + 1) % v.count or n & ((1 << 16) - 1), I see notational inefficiency. I first had an idea like this about a year ago, and I keep coming back to this same concept, nearly every time I work on something. It never doesn’t suck having to go back and add parentheses, and they tend to add to the visual clutter in any given expression.
Sure, there’s a base level of jank one has to live with when one writes math the way we do, but I really think the quality-of-life improvement that would come of adding a feature of this type, whatever the form it takes, would be well-worth the adjustment. I mean, this kind of expression is so common that we have a whole class of errors named after it, and even notation to dance around it (e.g. range operators).
You just gotta write it different or with fewer parentheses. Or not write it at all. Neither of those things should really be something you're encountering much in swift because of its higher-level collection APIs.
But ((1 << 16) - 1) is just 0xffff, and if it's not always 16 and you find yourself doing this with different parameters a lot, you can factor out a function with a somewhat meaningful name, like mask() or whatever.
To clarify Danny's point: in Swift the precedence of << is higher than the precedence of -, so technically speaking you don't need to use parens around 1 << 16. Although this is different compared to other languages like Objective-C, C, C#, Rust, Python, possibly others, making it not immediately obvious for people coming from those, so I admit I also tend to put those redundant parens (1 << 16) - 1.
On the postfix operator: you could certainly implement one yourself and use in your own code base, e.g.:
postfix operator +
postfix operator -
postfix func + <T: BinaryInteger> (v: T) -> T {
v + 1
}
postfix func - <T: BinaryInteger> (v: T) -> T {
v - 1
}
it just doesn’t seem valuable enough to be included in the standard library.
Compared to other feature proposals, the barrier for proposing a new operator is very high. Unless something has strong historic precedent in other languages, Swift prefers properties with fully descriptive names instead (e.g. there is no property named epsilon for Floats because it might be interpreted in different ways).
This is of course compensated by the fact that Swift has strong support for custom operators, and it's very easy to add this operator for just your own projects. As an example, I've implemented my own elvis assignment operator ?=, which performs just as well as any native solution (short-circuiting, correct operator precedence etc.)