5/16 lessons31%

Side Effects and Immutability

In functional programming, the concepts of side effects and immutability are tightly interwoven. Avoiding side effects and embracing immutability can drastically improve your code's reliability and maintainability. Let’s explore what these terms mean and why they are critical in functional programming.

Side Effects

A side effect occurs when a function interacts with or alters something outside its scope. Examples include:

  • Modifying a global variable.
  • Changing an input argument.
  • Performing I/O operations like reading/writing files or making API calls.

Side effects can make your code unpredictable and hard to debug.

Example of Side Effects:

javascript
1
2
3
4
5
6
7
8
          let counter = 0;

function incrementCounter() {
  counter++; // Modifies the external variable
}

incrementCounter();
console.log(counter); // 1 (Side effect occurred)
        

Pure Alternative:

javascript
1
2
3
4
5
6
7
8
          function increment(counter) {
  return counter + 1; // Doesn't modify external state
}

const counter = 0;
const newCounter = increment(counter);
console.log(newCounter); // 1
console.log(counter); // 0 (Unchanged)
        

Why Avoid Side Effects?

  1. 1
    Predictability: Functions behave consistently, making them easier to test.
  2. 2
    Reusability: Functions are modular and don’t rely on external factors.
  3. 3
    Parallelism: Safe to run in multithreaded or concurrent environments.

Immutability

Immutability means data cannot be changed once created. Instead of modifying an object or array, you create a new one. embrace Immutability:

  1. 1
    Prevents Bugs: Avoids unintended changes to shared data.
  2. 2
    Simplifies Debugging: No need to trace how or where data changed.
  3. 3
    Aligns with Pure Functions: Supports the creation of functions that don’t cause side effects.

Mutable vs. Immutable Objects

Mutable Approach (Imperative):

javascript
1
2
3
4
5
6
7
8
          let user = { name: "Alice", age: 25 };

function updateAge(user, newAge) {
  user.age = newAge; // Mutates the original object
}

updateAge(user, 26);
console.log(user); // { name: 'Alice', age: 26 } (Original object changed)
        

Immutable Approach (Functional):

javascript
1
2
3
4
5
6
7
8
9
          const user = { name: "Alice", age: 25 };

function updateAge(user, newAge) {
  return { ...user, age: newAge }; // Creates a new object
}

const updatedUser = updateAge(user, 26);
console.log(updatedUser); // { name: 'Alice', age: 26 }
console.log(user); // { name: 'Alice', age: 25 } (Original object remains unchanged)
        

Immutable Arrays

Mutable Approach:

javascript
1
2
3
4
5
6
7
8
          let numbers = [1, 2, 3];

function addNumber(numbers, newNumber) {
  numbers.push(newNumber); // Mutates the original array
}

addNumber(numbers, 4);
console.log(numbers); // [1, 2, 3, 4] (Original array changed)
        

Immutable Approach:

javascript
1
2
3
4
5
6
7
8
9
          const numbers = [1, 2, 3];

function addNumber(numbers, newNumber) {
  return [...numbers, newNumber]; // Returns a new array
}

const newNumbers = addNumber(numbers, 4);
console.log(newNumbers); // [1, 2, 3, 4]
console.log(numbers); // [1, 2, 3] (Original array remains unchanged)
        

Conclusion

Pure functions avoid side effects, and immutability ensures data integrity. Together, they create predictable and reliable programs.