3
$\begingroup$

In C-style languages we have:

int x = 10;
x++; // x is now 11 but the expression returns 10
int x = 10;
++x; // x is now 11 and the expression returns 11
int x = 10;
x += 2; // x is now 12 and the expression returns 12

But we don't have:

int x = 10;
x ??? 2; // x is now 12 but the expression returns 10

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? What are some syntax options to implement a post-augmented assignment operator, that evaluates to the old value of the left-hand operand?

$\endgroup$

3 Answers 3

6
$\begingroup$

Three things of historical note:

  • C was designed so that each operation closely corresponds to a hardware instruction.
  • C got its ++ and -- operators from the PDP-11 instruction set (deferred increment, and deferred decrement).
  • C allows assignment statements to be treated as expressions, since the instruction has already left the value available in a register.

There is no efficient or natural path from hardware instructions to the proposed operator (except for the case of adding or subtracting exactly 1), so there would have been no reason to invent such an operator.

In practice, the equivalent expression y = ((x+=n), (x-n)), where n is not 1 or -1, almost never occurs anywhere, so there is no demand for inventing syntactic sugar for it.
Even if it were common, it would be no more efficient than y = x ; x += 2 or ((x += 2), (y = x - 2)).

$\endgroup$
2
$\begingroup$

In some languages, such as Swift, assignment operators don't return anything in the first place. It's not super common to use an assignment (even just =) as an expression; I've typically seen it for two things:

  1. Chained assignment, a = b = c
  2. Assignment and null-check, if ((ptr = getThing())) ...

The compound assignment operators like += aren't conducive to these uses anyways. I don't think I've ever intentionally used the return value of a compound assignment operator.

$\endgroup$
1
$\begingroup$

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

$\endgroup$
3
  • 1
    $\begingroup$ As for the syntax, maybe something like 5 =+ x? $\endgroup$ Jun 14, 2023 at 3:16
  • $\begingroup$ @DannyuNDos Early versions of C did that for modern +=, or so I've heard, but the problem becomes that x=-10 is ambiguous -- x = -10 or x =- 10? $\endgroup$
    – Bbrk24
    Jun 14, 2023 at 23:39
  • $\begingroup$ Another issue with just using a mirror-image is that doesn't work for basic assignment like x = y, which is probably the most useful case (i.e. assign a new value to x and return the old value). $\endgroup$
    – kaya3
    Jun 14, 2023 at 23:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .