The ++ and -- operators are mutating and they do not exactly reverse each other. Quoting the 2017 standard:
12.4.4.1Runtime Semantics: Evaluation
UpdateExpression : LeftHandSideExpression ++
- Let
lhs be the result of evaluating LeftHandSideExpression.
- Let
oldValue be ? ToNumber(? GetValue(lhs)).
- Let
newValue be the result of adding the value 1 to oldValue, using the same rules as for the + operator (see 12.8.5).
- Perform
? PutValue(lhs, newValue).
- Return
oldValue.
It's that second step that's important, as it converts the value to a number primitive but there's also a subtle difference between that and a Number object as returned by the Number constructor.
var arr = [new Number(1234)];
function mutateAndRepair(arr) {
console.log(`the value before is ${arr[0]}`);
arr[0]++;
arr[0]--;
console.log(`the value after is ${arr[0]}`);
}
arr[0].foo = 'bar';
console.log(`foo before is ${arr[0].foo}`);
mutateAndRepair(arr)
console.log(`foo after is ${arr[0].foo}`);
Now, I'm being a little cheeky here by loosely interpreting your requirement that the first item of arr is a "number". And for sure, you can add another stipulation that the values of arr must be "number primitives" to exclude this exact form of mutation.
How about another, more subtle point. -0 and 0 are treated as the same value in virtually all ways except Object.is:
var arr = [-0];
function mutateAndRepair(arr) {
console.log(`the value before is ${arr[0]}`);
arr[0]++;
arr[0]--;
console.log(`the value after is ${arr[0]}`);
}
console.log(`is zero before ${Object.is(0, arr[0])}`);
mutateAndRepair(arr)
console.log(`is zero after ${Object.is(0, arr[0])}`);
Okay, you can add a requirement that the first item of arr is not -0. But all of that kind of misses the point. You could argue that virtually any method is non-mutating if you simply declare that you're going to ignore any case in which mutation would be observed.
Considering the restraints does this comply with the common functional paradigm used by JavaScript coders?
I would not consider this code to follow functional coding principles, and would perhaps even reject it in a code review if that were a goal of the project. It's not even so much about the nitty-gritty of how or whether immutability is assured by all code paths, but the fact that it depends upon mutation internally that makes this code non-functional in my view. I've seen a number of bugs arise in pseudo-functional code where an exception occurs between the mutate and repair steps, which of course leads to clear and unexpected side-effects, and even if you have a catch/finally block to try to restore the state, an exception could also occur there. This is perhaps just my opinion, but I think of immutability as a part of a larger functional style, rather than just a technical feature of a given function.