Course outline
Full-Stack course / JavaScript fundamentals

Pure pipeline

Lab

How to fetch and submit this exercise

Once: install the tool and connect your account.

curl -fsSL https://gonomicon.com/install.sh | sh
gonomicon login

Each exercise: download it, edit the file named in the instructions, then submit.

gonomicon fetch fs-week-2/02-07-pure-pipeline
cd 02-07-pure-pipeline
gonomicon submit

The runner depends on the exercise: JavaScript/TypeScript/React labs run with Vitest, Python labs with pytest, and config-file labs (Docker, CI, Terraform, Nginx) are checked by parsing your file. gonomicon test runs the visible checks on your computer first; submitting also runs the hidden checks on our server.

Pure pipeline

Rewrite a function that secretly depends on the outside world into one that only uses its own inputs, then write a function that chains several small steps into one.

Worked example

Here's an impure function, the way the starter's addTax is impure:

let greetCount = 0;

function shout(word) {
  greetCount++;
  console.log(`${word.toUpperCase()}!`);
}

shout changes greetCount (a box outside itself) and prints instead of returning anything - call it twice with the same word and you can't even get the result back, only watch greetCount climb. The pure rewrite uses only what's handed in, and returns the answer instead of printing it:

function shout(word) {
  return `${word.toUpperCase()}!`;
}

Now here's compose, chaining pure steps together:

function compose(...fns) {
  return function piped(input) {
    let result = input;
    for (const fn of fns) {
      result = fn(result);
    }
    return result;
  };
}

const trim = (text) => text.trim();
const exclaim = (text) => `${text}!`;
const shoutLoud = compose(trim, shout, exclaim);

console.log(shoutLoud("  hello "));
console.log(compose()("unchanged"));
HELLO!!
unchanged

shoutLoud runs trim, then shout, then exclaim, each one feeding its result to the next. compose() with nothing to chain just hands back whatever it's given.

Your task

Rewrite the starter's impure addTax(price) as a pure function addTax(price, percent) that returns price + price * percent / 100 using only its two parameters - no outside variable, and nothing printed.

Write function compose(...fns), exactly as in the worked example's pattern: it returns a function that passes its input through every function in fns, left to right, and with no functions at all it returns the input unchanged.

Rules

  • addTax must not read taxPercent, write lastTotal, or call console.log - delete all three from the file along with the old one-parameter version.
  • addTax(price, percent) gives the same answer every time it's called with the same two arguments, no matter what was called before it or what order things ran in.
  • compose's returned function calls each function in fns exactly once per call, left to right, each one getting the previous one's result.
  • Neither addTax nor compose may print anything or change a variable that lives outside the function.

What you'll practice

Turning a function with side effects into a pure one by passing in everything it needs, and writing a higher-order function that chains functions together using a closure over the list it was given.

Run it

gonomicon fetch fs-week-2/02-07-pure-pipeline
cd 02-07-pure-pipeline
gonomicon test
gonomicon submit

Hints

  1. A pure addTax should only ever look at price and percent - if your answer changes when some outside variable changes, something outside is still being read.
  2. compose(...fns) can keep a running value in a loop: start it as the input, then for each fn in fns, replace it with fn(the running value). With no fns, the loop runs zero times and the input comes back untouched.
  3. Delete taxPercent, lastTotal and the console.log call entirely rather than just not using them - the finished file shouldn't mention them at all.