Dev Genius

Coding, Tutorials, News, UX, UI and much more related to development

Follow publication

How to write better JavaScript using plumbing techniques

--

Photo by Samuel Sianipar on Unsplash

A mind is a terrible thing to waste, and one of the fastest ways to do it is not keeping it open enough. Although most of my development career I’ve spent writing in JavaScript and I love this language, I’ve never considered myself a JavaScript developer. I never had problems with writing in Java, Python, or C# when there was a need, and I also like to explore languages that I don’t have knowledge about and try to get the best out.

This was exactly the case with Elixir — one of my colleagues in my previous company talked me into taking an online course about Elixir, and I liked it very much — it has cool language features like list comprehensions, advanced pattern matching, but the one that I’ve resonated most with was definitely the pipe operator. Unfortunately, I didn’t have much opportunity to write in Elixir at work, but I found ways to use what I’ve learned while writing in JavaScript. Read on to find out!

Photo by Abhi Bakshi on Unsplash

The first concept that I’d like to make you familiar with to get going is function currying (the name, surprisingly, not coming from a popular Indian dish, but rather from an American mathematician and logician, Haskell Curry). I’ll explain what it’s useful for in a moment, for now all that we need to know is that currying a function with a fixed number of arguments makes it possible to pass arguments in more than one call. Seems complicated? Perhaps the example will make it clear:

Of course, we’d need to either implement the curry function ourselves, or — like in the example above — use an existing implementation, in this case, the one that’s part of a functional library called ramda, which I will also use in some of the next examples.

Photo by Gabriel Barletta on Unsplash

The second useful concept is function composition. What’s that about? According to the mathematical definition, if there’s a function f(x) and another g(x), it’s possible to compose them into a function h(x) = g(f(x)). Let’s imagine that we need to add two to a number, then raise the result to the power of two, and from the resulting number subtract three.

The code isn’t that complex, but it’s really not that apparent what is going on in there at first sight; the readability is not great. Also, what happens if we need to add a similar calculation, but with slightly different parameters (like adding five, raising to the power of three, etc.)? Apart from not being readable, the code is also not very reusable. What can we do to improve it? Let’s start with extracting all the atomic actions.

This would allow us to write the code in a bit more reusable way:

This definitely is more reusable, but still not very readable — and we’re only doing three modifications to the input, what would happen if we had more? Let’s try to make that more understandable:

What happened here? We took three functions, like f(x), g(x) and h(x), and we’ve created a brand new function that’s a composite of the three:
k(x) = h(g(f(x))). The order of execution is exactly like in the mathematical definition — from inside out. We start with the last function that we want to compose and proceed towards the first. While some people like that approach, I consider it counterintuitive — in my opinion, it needs an additional mind cycle to adjust. Luckily, there’s a simple way to make it a bit more straightforward:

What we see now is a bit more natural — the order of execution is the same as the natural reading order — we know exactly what’s happening at first glance. Why pipe? It’s simply because we pipe data through consecutive functions to get the final result. Can it be simplified even further? Let’s go back to our atomic functions for a moment.

Two apparent things happened in here. First, we’ve reversed the order of arguments in each function (it actually isn’t needed for add — because of commutativity of addition — but this way all the functions are consistent), second, we’ve currified all the functions. What for? Let’s take a look at how this can affect our calculations:

The first thing that’s clearly visible is that this function is now readable as plain English — we add two, then raise to the power of two, and subtract three! This is possible mostly thanks to currying. Let’s take a look at the power function:

As you can see, currified power can now accept two arguments at once or — if only supplied with the first one, which is now the exponent, returns a function that accepts a base and raises it to the previously specified power.

What is that good for? Well, in my opinion — first and foremost it increases readability, composability, and maintenance. The improvement of readability is unquestionable — if the piped functions are named correctly (or semantically), the code should read like plain English. Composability gains are also pretty obvious — if there’s ever a need to do raise a number to the power of five and then add seven, it wouldn’t be required to write the whole code from scratch. The same goes with maintainability — if there’s a need to add three instead of two, it’s instantly clear how to change the values in the pipe, whereas it’s pretty easy to make a mistake while editing Math.pow(myNumber + 2, 2) — 3.

Of course, those examples are very simple and the benefits might not be obvious at first glance, but it’s not uncommon to have much, much more complicated atomic functions — for example in image processing (eg. all the operations on the image data mentioned in this article: https://medium.com/jit-team/how-to-create-a-musical-instrument-with-no-notes-using-javascript-ec6a83333aa4 would make up for an excellent pipe).

I hope that you’ll find any of this useful — if so (or not!), please let me know in the comments!

I mentor software developers. Drop me a line on MentorCruise for long-term mentorship or on CodeMentor for individual sessions.

Sign up to discover human stories that deepen your understanding of the world.

--

--

No responses yet

Write a response