The one problem I’ve been running into with pipelines is debugging — I’ll have a test tell me a pipeline is outputting unexpected data, but I’m not sure where the problem is.
For instance, let’s say you’re writing a function to get the average area of a list of triangles. Each triangle is represented as an array of
[height, width]. Knowing the formula for the area of a triangle is
width * height / 2, you write a little pipeline function like this:
Nice and clean, looks reasonable. Then you try testing the code:
Expected 0.17824074074074073 to be close to 8.167. Boo.
So at this point, what I want to do is put a
console.log after every step, and see how the data is changing along the pipeline. That’s easy enough; Ramda has
tap (and RxJS has
.do). They both let you pass a function that’ll perform a side effect, but then will return the original value. That lets us do this:
So now when we run our test, we get some extra output:
console.log, but then we can’t just pass
console.log as a callback anymore, we’d have to do something like this:
That’s fine, but typing those fat arrow functions, so annoying! Happily, there’s a solution for the lazy programmer — a curried
On my last project, we added a tiny helper function for this very problem.
log takes any number of arguments to tag your log with, and then a last argument that’s your actual data, passes it all to
console.log, and returns the original data. So now we can really clean up our code, to just:
log takes a value, performs a side effect with it, and then returns a value. That sounds like
tap! And indeed, we can drop the calls to
tap, and just use
Nice. And now looking at the test output, we see it’s definitely the division step that’s failing:
It turns out that Ramda’s
divide function takes the numerator as the first argument, while we’re trying to pass it the denominator, 2. So
const divideByTwo = R.divide(2) should be
const divideByTwo = R.flip(R.divide)(2).
And now our test reports success!
log also works well with RxJS’s
So there you go! A handy way to inject some loggers into functional pipelines with a minimum of boilerplate.