I know older versions of Swift had successor() and predecessor() methods that went one unit forward or backward. They were kind of like C's ++ and -- operators, and so were retired when we moved away from C for-looping. Now I wonder if BinaryInteger should add similar operations in case it can model a single increment faster than the full addition routine with a second operand of 1. (Of course, an alternative is to check for 1 in the addition routine then special-case optimize.)
succeed: += 1
precede: -= 1
we could go further:
double: <<= 1
halve: >>= 1
reset: %= 1
(Can you figure out why I didn't add multiplication or division versions?)
That could help with the default integer types, which map to the processor's integer primitives, but that wouldn't help with user-defined integer types, especially those that don't shadow processor primitives.
As I understand it from threads here with posts from Swift big-wigs, adding a customization point with a corresponding default implementation is not ABI breaking. There have been several similar suggestions before. It is ABI-breaking to remove customization points (obviously) or to add new base or intermediate protocols (even if you maintain source compatibility).
There's nothing magical about adding or subtracting 1 that you can do significantly more efficiently than adding or subtracting, say, 2. Even if there were, the compiler should be able to see through what you're doing and optimize accordingly. I don't see any need for these operations on the protocol.
What's the concern here, that some custom type will have a fast-path for increment/decrement and without a customization point the compiler won't generate the best code?
If such a fast-path exists and is implemented by that type in the regular addition method, the optimizer will handle it fine. Here's an example—the conditional gets optimized away and the caller didn't have to do anything special, which is what we want:
struct SillyInt: BinaryInteger {
private let value: Int
// ...
static func += (lhs: inout SillyInt, rhs: SillyInt) {
if rhs.value == 1 { // FAST PATH
lhs.makeOneBigger()
} else {
lhs = SillyInt(lhs.value + rhs.value)
}
}
@inline(never) // so we see it in the output
mutating func makeOneBigger() { self = .init(self.value + 1) }
// ...
}
func myFunc() -> SillyInt {
var mySillyInt: SillyInt = 5
mySillyInt += 1
return mySillyInt
}