Mutability: Array

Mutability in programming is the ability of an object to have it’s state / value changed. Immutable object, on the other hand, cannot have it’s state changed. In php, javascript, C# and Java, most of variables / objects that is commonly used are mutable. Array is one of them.

Array mutability

Let’s see the following snippet of code:

let exec1 = function() {
    console.log("START: EXEC 1");
    let a = [3, 5, 7];
    let wrongOperation = function(arr){
        arr.push(8);
        arr[1] = 4;

        return arr;
    };

    let b = wrongOperation(a);
    console.log("b:");
    b[2] = 6; // mutation
    console.log(b); // results [ 3, 4, 6, 8]

    console.log("a:");
    console.log(a); // results [ 3, 4, 6, 8]
    console.log("DONE: EXEC 1");
    console.log();
};

We can see that any modification to b is changing the value of a, which sometimes is expected, and the other times are becoming a bug. If the result b are returned and modified by another caller. Sometimes in the future, you may wonder why the content of a changed. It will be hard to track where the changes happen to a.

A better design

A better design is indeed, to prevent any changes made to b to be reflected back to it’s original owner, a. This can be achieved by replicating the array argument using concat in javascript, or array_merge in ​php to an empty array. See the example in following snippet:


let exec2 = function() {
    console.log("START: EXEC 2");
    let a = [3, 5, 7];
    let correctOperation = function(arr){
        let result = [].concat(arr);
        result.push(8);
        result[1] = 4;

        return result;
    };

    let b = correctOperation(a);
    console.log("b:");
    console.log(b); // results [ 3, 4, 7, 8]

    console.log("a:");
    console.log(a); // results [ 3, 5, 7]
    console.log("DONE: EXEC 2");
    console.log();
};

Above example shows how the operation copy the argument array first before doing any operation using concat with another empty array. It cause any further modification after that function to not reflect back to original variable a.

Another example can be like this:


let exec3 = function() {
    console.log("START: EXEC 3");
    let a = [3, 5, 7];
    let anotherCorrectOperation = function(arr){
        let newData = [];
        for(let i = 2; i < 4; i++){
            newData.push(i);
        }
        return arr.concat(newData);
    };

    let b = anotherCorrectOperation(a);
    console.log("b:");
    b[1] = 4; // test mutability
    console.log(b); // results [ 3, 4, 7, 2, 3]

    console.log("a:");
    console.log(a); // results [ 3, 5, 7]
    console.log("DONE: EXEC 3");
    console.log();
};

The above example do operations first, then returning the operation result together with the existing array. This is the preferred approach to the other for-push​ directly to argument array.

It’s still just the array that be copied

However, both preferred example above only copied and un-ref the array and array only. The content is still the same, and can be modified. For example, if the array contains javascript objects, any modification to the array member will be reflected back to original variable a:

let exec4 = function() {
    console.log("START: EXEC 4");
    let a = [{v: 1}, {v: 3}];
    let anotherCorrectOperation = function(arr){
        let newData = [];
        for(let i = 2; i < 4; i++){
            newData.push({v: i});
        }
        return arr.concat(newData);
    };

    let b = anotherCorrectOperation(a);
    console.log("b:");
    b[1].v = 4; // test mutability
    console.log(b); // results [ { v: 1 }, { v: 4 }, { v: 2 }, { v: 3 } ]

    console.log("a:");
    console.log(a); // results [ { v: 1 }, { v: 4 } ]
    console.log("DONE: EXEC 4");
    console.log();
};

So you still need to be careful at doing variable modification inside functions. However at least you need not to worry anymore when doing modification to the array.

Conclusion

Design your operation to be immutable and returning copy by default, unless the other behavior is somewhat desired. This can help to make code easier to track, modular and prevent unnecessary bug in the future. All code that is shown in this article can be retrieved at my github repository.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s