Deubugging functional JavaScript

R.tap to the rescue

Functional approach lets you write concise and expressive programs. It is all good until something breaks until some of your assumptions are broken.

JavaScript interpreter will not help you finding the most common problems. Those are misaligned assumptions. You thought that the thing returned will be of one type, shape, size but you got something else. It’s a reality of programming in such environment.

When programming functionally regular debuggers are not that helpful. Your expressive code is composed of a lot of small functions calling each other. Usually, the arguments are passed directly to the next caller (point free style), so there’s nothing to inspect.

An initial approach could be to just disassemble expressive bit of code. Make all the values you are interested in explicit. Then use standard tools to tackle the problem and to find where your assumptions were wrong.

Usually such approach requires tedious and uninspired code shuffling. This destroys the beauty of the concise code. One might be tempted to not assemble it back, just in case of another problem popping up in the same place.

But there’s a better way.

The great functional library Ramda contains a function specially designed to handle that problem. It’s unassuming tap.

It accepts two arguments: - function to run against the argument, - the argument.

What it does it first runs the function from the first argument with the second argument and then returns the second argument. That description may not give you an obvious idea where you could use it, but I hope the following example will make it clear.

Let’s assume you have a function total which should return a total for a bill.

const total = R.compose(
  R.sum, R.props(['amount', 'tax', 'tip']));

It should work in the following way

total({amount: 100.0, tax: 20.0, tip: 20.0});
// => 140

But instead of the expected number you are getting back a NaN. That probably means that one of the values passed to the R.sum is not a number, but which?

The quick way to find out is to use R.tap. I usually define myself a small helper function peek:

const peek = R.tap(console.log);

That way I can just stick it in the middle of the compose chain and see what value was passed from one function to another.

const total = R.compose(
  R.sum, peek, R.props(['amount', 'tax', 'tip']));

And with that, I can actually peek inside of the function. The change is minimal and can be undone trivially.

It can also be used to peek what exactly is being returned from from a function by wrapping the result passed to the returns statement. Especially if the returned value is something more complicated. Like that:

function something() {
  // complicated piece calculating items
  return peek(R.map(addFoo, items));
}

It helped me many times to diagnose problems. And each time I didn’t have to shuffle a lot of code.