← Back to all posts

A Simple Currying Refactor: Remove Pass-Through Arguments

Sharpen Currying Skills

4 parts
  1. Higher-order functions for currying in JavaScript
  2. Predicate Functions in JavaScript
  3. Currying in JavaScript
  4. A Simple Currying Refactor: Remove Pass-Through Arguments
A Simple Currying Refactor: Remove Pass-Through Arguments

Like I said at the beginning of this series, I wanted refactoring existing code to use currying and partial application to feel natural and intuitive. This heuristic is a big part of that.

This post covers one simple heuristic for spotting those cases. By the end, you should be able to look at a small wrapper function and tell whether it can be replaced with partial application.

What it is

If a parameter is only passed as it is to another function, the wrapper is probably not doing anything useful. In many cases, you can remove that extra parameter and keep the partially applied function instead.

Let’s see this in action with a simple example:

Before:

const split = separator => str => str.split(separator);
const words = str => split(" ")(str);

// Usage:
console.log(words("hello world")); // ["hello", "world"]

Here, words takes str and passes it directly to split(" "). It does not change str, and it does not add any new behavior.

So the wrapper is not really needed. We can keep the partially applied function itself:

After:

const split = separator => str => str.split(separator);
- const words = str => split(" ")(str);
+ const words = split(" ");

// Usage remains the same:
console.log(words("hello world")); // ["hello", "world"]

The nice part is that usage stays the same. We still call words("hello world"), but now the implementation is smaller and more direct.

Why it matters

This matters because this kind of wrapper shows up a lot, and after some time it just adds extra code for no real benefit. Once you start noticing it, many refactors become easier and cleaner.

The mechanics

Here is the idea:

  1. Look for a parameter that is only passed unchanged. In the example above, that is str.
  2. Check if the wrapper is doing anything else besides passing that value.
  3. If not, remove the wrapper parameter and keep the partially applied function.

Safety checklist

  • The argument is passed through unchanged.
  • The wrapper does not transform the argument in any way or use it for anything else.
  • The function you call supports currying in that argument order.
  • The result is easier to read, not harder.

Exercises

To get the best out of this series, I recommend spending 15-25 minutes solving the exercises on your own, then checking the answers.

1. Remove pass-through from words

Refactor:

const split = separator => str => str.split(separator);

const words = str => split(" ")(str);
Show answer
const split = separator => str => str.split(separator);

const words = split(" ");

2. Remove pass-through from filterQs

Refactor:

const filter = predicate => xs => xs.filter(predicate);

const filterQs = xs => filter(x => /q/i.test(x))(xs);
Show answer
const filter = predicate => xs => xs.filter(predicate);

const filterQs = filter(x => /q/i.test(x));

3. Decide whether to refactor this wrapper

Decide whether to refactor and explain why:

const clean = str => trim(str).toLowerCase();
Show answer

Do not refactor with this heuristic. str is not a pass-through argument because it is used in trim(str) and then transformed again by .toLowerCase().

Checkpoint

You are ready to move on when you can look at a small wrapper and quickly tell if it is doing real work or just passing data through.

Previous article Currying in JavaScript