Home

Abstractions

Abstractions are when we stop thinking of code as the exact problem its solving, but the generalized class of problem it can solve.
Just as we might want to upgrade a part in your computer, we may want to upgrade (or modify) parts of a codebase. How can we do so with the least amount of obstruction, balanced with the upfront and maintenance costs of such a "generalized part"?

🔗 By example

My favorite example of abstraction is Data Abstraction, spelled out in Chapter 2 of Structure and Interpretation of Computer Programs.
Consider two functions which add and multiply rational numbers p/qp/q.
add(p1q1,p2q2)=p1q1+p2q2=p1q1×q2q2+p2q2×q1q1=p1q2q1q2+p2q1q2q1=p1q2+p2q1q1q2multiply(p1q1,p2q2)=p1p2q1q2\begin{aligned} add(\frac{p_1}{q_1}, \frac{p_2}{q_2}) &= \frac{p_1}{q_1} + \frac{p_2}{q_2} \\ &= \frac{p_1}{q_1} \times \frac{q_2}{q_2} + \frac{p_2}{q_2} \times \frac{q_1}{q_1} \\&= \frac{p_1 q_2}{q_1 q_2} + \frac{p_2 q_1}{q_2 q_1} \\&= \frac{p_1 q_2 + p_2 q_1}{q_1 q_2} \\ multiply(\frac{p_1}{q_1}, \frac{p_2}{q_2}) &= \frac{p_1 p_2}{q_1 q_2} \end{aligned}
How might we store our rational numbers? Let's use an array.
const oneHalf = [1, 2]; // 1 / 2

function add(a, b) {
  const [p1, q1] = a;
  const [p2, q2] = b;

  return [p1 * q2 + p2 * q1, q1 * q2];
}

function multiply(a, b) {
  const [p1, q1] = a;
  const [p2, q2] = b;

  return [p1 * p2, q1 * q2];
}

console.log(add([1, 2], [1, 3]));
// => [5, 6]
console.log(multiply([1, 2], [1, 3]));
// => [1, 6]
Maybe down the road we decide arrays are silly and we prefer objects.
const oneHalf = { numerator: 1, denominator: 2 }; // 1 / 2
Let's try our code:
console.log(
  add({ numerator: 1, denominator: 2 }, { numerator: 1, denominator: 3 })
);

// data-abstraction.js:4
//   const [p1, q1] = a;
//                    ^

// TypeError: a is not iterable
No worries, let's just change up add:
 function add(a, b) {
-  const [p1, q1] = a;
-  const [p2, q2] = b;
+  const { numerator: p1, denominator: q1 } = a;
+  const { numerator: p2, denominator: q2 } = b;
 
-  return [p1 * q2 + p2 * q1, q1 * q2];
+  return { numerator: p1 * q2 + p2 * q1, denominator: q1 * q2 };
 }
And we run again:
 data-abstraction.js:11
  const [p1, q1] = a;
                   ^

TypeError: a is not iterable
Not to worry, we just need to change multiply too.
 function multiply(a, b) {
-  const [p1, q1] = a;
-  const [p2, q2] = b;
+  const { numerator: p1, denominator: q1 } = a;
+  const { numerator: p2, denominator: q2 } = b;
 
-  return [p1 * p2, q1 * q2];
+  return { numerator: p1 * p2, denominator: q1 * q2 };
 }
Now our code works 🎉
$ node script.js 
{ numerator: 5, denominator: 6 }
{ numerator: 1, denominator: 6 }

🔗 Now do it all again

You push your change, get it reviewed, and merge it in. Now our fractions are represented as { numerator, denominator } and we can move onto the next Jira ticket.
...until a