Why would it make sense to allow an inline operation to return a value before adding a value, but only if the value you are adding is one?
One significant use-case is writing to an array or buffer: you want to store a value at the current index, while incrementing the index, but the value should be stored at the old index.
arr[n++] = something;
This is preferable to the alternative of pre-incrementing the index, for two reasons: firstly because it allows the index n
to be initialised as 0 instead of −1 (zero is more natural, and the index is often an unsigned integer), and secondly because if n
is always the next index to be used then it happens to also equal the total number of indices in use, i.e. the length of the sequence.
Likewise, another significant use-case is assigning unique, incrementing IDs to some objects. The arguments in favour of post-incrementing over pre-incrementing are the same. In both use-cases, what matters is that the variable ranges over consecutive increasing integers, so naturally the number you want to add is 1.
What are some syntax options to implement a post-augmented assignment operator, that evaluates to the old value of the left-hand operand?
I don't know of any popular languages which have a syntax for this, and I doubt there are any; it seems too niche to be worth having a dedicated syntax for. (The most useful case might actually be for regular assignment, instead of augmented assignment.) So if you do want to add it to your language, there's probably no good precedent to borrow syntax from.
One option would be to use some special symbol to denote that an assignment should evaluate with the old value; something like x += 5
could be a pre-add-assign, while x @+= 5
, x $+= 5
or x #+= 5
could be a post-add-assign. The problem with this approach is that it won't make sense to readers who aren't familiar with the language, and it will be hard to search for in order to find out (e.g. Google search doesn't handle punctuation).
Another option which would be possible in some languages would be to make the operation as a function or method, so that it has a name. This would work in languages where a function can accept a reference to a variable, so that it can mutate the variable via the reference. For example, in Rust, it's even possible for the user to add this behaviour to the built-in numeric types:
trait PostAddAssign<Rhs = Self> {
fn post_add(&mut self, rhs: Rhs) -> Self;
}
impl <T, Rhs> PostAddAssign<Rhs> for T
where T: std::ops::AddAssign<Rhs> + Copy {
fn post_add(&mut self, rhs: Rhs) -> Self {
let old = *self;
*self += rhs;
old
}
}
Rust playground link