Functional Programming Part 6 - Extract Calculations from Actions

Identifying Actions and calculations within functions, and attempting refactoring...

November 30, 2025

In the previous article Functional Programming Part 5 - Identify Actions, we practiced identifying Actions through actual code. Now, let's take it a step further and extract Calculations from within Actions.

The book demonstrates this through three code examples, showing how to identify which parts of a large function are Actions, which are Calculations, or Data, and then reasonably extract parts that can be pulled out as pure functions. Here is the original code:

/**
 * Code 1: This code demonstrates that when a user adds items to the shopping cart, they can simultaneously see the total amount change
 */
const shoppingCart = [];
const shoppingCartTotal = 0;
 
 
function addItemToCart(name, price) {
  shoppingCart.push({
    name,
    price,
  });
 
  calcCartTotal();
}
 
function calcCartTotal() {
  shoppingCartTotal = 0;
  for (let i = 0; i < shoppingCart.length; i++) {
    const item = shoppingCart[i];
    shoppingCartTotal += item.price;
  }
  setCartTotalDOM(); // Update DOM to display total amount
}
 
/**
 * Code 2: Calculate free shipping - if exceeding the free shipping threshold ($20), no shipping fee is charged
 * If adding item A to the cart exceeds the threshold, item A will display a FREE logo, otherwise it won't
*/
 
function updateShippingIcons(){
  const buyButtons = getBuyButtons();
  for (let i = 0; i < buyButtons.length; i++) {
    const button = buyButtons[i];
    const item = button.item;
    if (item.price + shoppingCartTotal > 20) {
      button.showFreeShippingIcon();
    } else {
      button.hideFreeShippingIcon();
    }
  }
}
 
function calcCartTotal() {
  shoppingCartTotal = 0;
  for (let i = 0; i < shoppingCart.length; i++) {
    const item = shoppingCart[i];
    shoppingCartTotal += item.price;
  }
  setCartTotalDOM();
  updateShippingIcons();
  updateTaxDOM();
}
 
/**
 * Code 3: Calculate tax
*/
 
function updateTaxDOM() {
  setTaxDOM(shoppingCartTotal * 0.10);
}

First, Identify Actions, Calculations, and Data

Let's review the definitions of Actions, Calculations, and Data:

const shoppingCart = []; // A -> will change at any time
const shoppingCartTotal = 0; // A -> will change at any time
 
 
function addItemToCart(name, price) {
  shoppingCart.push({ // A -> modifies global variable
    name,
    price,
  });
 
  calcCartTotal();
}
 
function updateShippingIcons(){
  const buyButtons = getBuyButtons(); // A -> gets DOM elements
  for (let i = 0; i < buyButtons.length; i++) {
    const button = buyButtons[i];
    const item = button.item;
    if (item.price + shoppingCartTotal > 20) {
      button.showFreeShippingIcon(); // A -> modifies DOM
    } else {
      button.hideFreeShippingIcon(); // A -> modifies DOM
    }
  }
}
 
function updateTaxDOM() {
  setTaxDOM(shoppingCartTotal * 0.10); // A -> modifies DOM
}
 
function calcCartTotal() {
  shoppingCartTotal = 0; // A -> modifies global variable
  for (let i = 0; i < shoppingCart.length; i++) {
    const item = shoppingCart[i];
    shoppingCartTotal += item.price;
  }
  setCartTotalDOM();
  updateShippingIcons();
  updateTaxDOM();
}

Basically, all of these are Actions, because if there's even one Action within a function, the entire function becomes an Action 😅

Function Inputs and Outputs

Function inputs and outputs can be explicit (explicit input and output) or implicit (implicit input and output), also known as side effects. What's the difference between these implicit and explicit inputs and outputs?

Explicit inputs and outputs refer to passed arguments and return values, while everything else is implicit input and output. For example:

function addToTotal(amount) {
  console.log(amount);
  total += amount;
  return total;
}

In the above code, the argument is amount and the return value is total - these are the explicit inputs and outputs. Additionally, console.log(amount) and total += amount are implicit inputs and outputs because they're not explicit inputs and outputs, but rather additional behaviors.

If a function only has explicit inputs and outputs, it's called a pure function. Otherwise, it's called an impure function.

Refactoring the Above Code

/**
 * Before Refactoring ========================================================
*/
function calcCartTotal() {
  shoppingCartTotal = 0;
  for (let i = 0; i < shoppingCart.length; i++) {
    const item = shoppingCart[i];
    shoppingCartTotal += item.price;
  }
  setCartTotalDOM();
  updateShippingIcons();
  updateTaxDOM();
}
 
/**
 * After Refactoring v1 ========================================================
*/
 
// Still an Action
function calcTotal() {
  shoppingCartTotal = 0; // output
  for (let i = 0; i < shoppingCart.length; i++) { // shoppingCart.length input
    const item = shoppingCart[i];
    shoppingCartTotal += item.price; // output
  }
}
 
function calcCartTotal() {
  calcTotal();
  setCartTotalDOM();
  updateShippingIcons();
  updateTaxDOM();
}
 
/**
 * After Refactoring v2 ========================================================
*/
 
function calcTotal(cart) {
  let total = 0; // change global variable to local variable
  for (let i = 0; i < cart.length; i++) { // pass cart as argument, don't use global variable
    const item = cart[i];
    total += item.price;
  }
  return total; // return local variable
}
 
function calcCartTotal() {
  shoppingCartTotal = calcTotal(shoppingCart);
  setCartTotalDOM();
  updateShippingIcons();
  updateTaxDOM();
}

Refactoring of another code section:

/**
 * Before Refactoring ========================================================
*/
function addItemToCart(name, price) {
  shoppingCart.push({
    name,
    price,
  });
  calcCartTotal();
}
 
/**
 * After Refactoring v1 ========================================================
*/
 
function addItem (cart, name, price) {
  cart.push({ // pass cart as argument, don't use global variable, but push here will modify the global array
    name,
    price,
  });
}
 
function addItemToCart(name, price) {
  addItem(shoppingCart, name, price); 
  calcCartTotal();
}
 
/**
 * After Refactoring v2 ========================================================
*/
 
function addItem (cart, name, price) {
  const newCart = [...cart]; // or const newCart = cart.slice(); 
  newCart.push({
    name,
    price,
  });
  return newCart;
}
 
function addItemToCart(name, price) {
  shoppingCart = addItem(shoppingCart, name, price); 
  calcCartTotal();
}

One way to implement Immutability is through Copy-on-write, where "write" refers to writes within the function, not global writes.

Testing the Above Calculations

function calcTotal(cart) {
  let total = 0;
  for (let i = 0; i < cart.length; i++) {
    const item = cart[i];
    total += item.price;
  }
  return total;
}
 
console.log(calcTotal([{ price: 10 }, { price: 20 }])); // always returns the same result: 30
console.log(calcTotal([{ price: 10 }, { price: 20 }, { price: 30 }])); // always returns the same result: 60
 
function addItem(cart, name, price) {
  cart.push({
    name,
    price,
  });
}
 
console.log(addItem([{ price: 10 }, { price: 20 }], 'apple', 10)); // always returns the same result: [{ price: 10 }, { price: 20 }, { name: 'apple', price: 10 }]
console.log(addItem([{ price: 10 }, { price: 20 }], 'banana', 20)); // always returns the same result: [{ price: 10 }, { price: 20 }, { name: 'banana', price: 20 }]

Steps to Extract Calculations from Actions:

Back to Blog 🏃🏽‍♀️