The provided content discusses the concept of function composition in JavaScript, emphasizing its growing importance in functional programming and its practical application in writing more readable and maintainable code.
Abstract
Function composition is a key concept in functional programming that involves combining multiple functions to create a new function. This technique is increasingly prevalent in modern JavaScript development, as it allows for a more declarative and less error-prone coding style. The article illustrates the transformation of a user's full name into a URL slug as an example of function composition, demonstrating how to refactor the code using utilities from Lodash/fp for better readability and maintainability. It also introduces the compose() and pipe() functions, which help in chaining operations in a more intuitive sequence, and discusses the benefits of a points-free style that eliminates the need for temporary variables. The author, Eric Elliott, advocates for the use of functional programming principles, including partial application and currying, to enhance code quality and reduce boilerplate.
Opinions
The author believes that functional programming is becoming the standard in large JavaScript applications.
Eric Elliott suggests that the use of compose() and pipe() from Lodash/fp can significantly improve the readability of function compositions.
He argues in favor of a points-free style for writing functions, which can lead to more abstract and generalized code with less focus on variable names.
The author acknowledges that while points-free style can be beneficial, it should be used judiciously to avoid creating code that is too dense and difficult to understand.
Elliott provides practical examples and encourages the use of trace() and tap() to make the flow of data through composed functions easier to follow and debug.
He promotes the idea that mastering functional programming concepts like function composition is essential for candidates interviewing for mid to senior-level JavaScript positions.
Master the JavaScript Interview: What is Function Composition?
Google Datacenter Pipes — Jorge Jorquera — (CC-BY-NC-ND-2.0)
“Master the JavaScript Interview” is a series of posts designed to prepare candidates for common questions they are likely to encounter when applying for a mid to senior-level JavaScript position. These are questions I frequently use in real interviews.
Functional programming is taking over the JavaScript world. Just a few years ago, few JavaScript programmers even knew what functional programming is, but every large application codebase I’ve seen in the past 3 years makes heavy use of functional programming ideas.
Function composition is the process of combining two or more functions to produce a new function. Composing functions together is like snapping together a series of pipes for our data to flow through.
Put simply, a composition of functions `f` and `g` can be defined as `f(g(x))`, which evaluates from the inside out — right to left. In other words, the evaluation order is:
`x`
`g`
`f`
Let’s look at this more closely in code. Imagine you want to convert user’s full names to URL slugs to give each of your users a profile page. In order to do that, you need to walk through a series of steps:
split the name into an array on spaces
map the name to lower case
join with dashes
encode the URI component
Here’s a simple implementation:
Not bad… but what if I told you it could be more readable?
Imagine each of these operations had a corresponding composable function. This could be written as:
This looks even harder to read than our first attempt, but hang in there, this is going somewhere.
In order to accomplish this, we’re using composable forms of common utilities like `split()`, `join()` and `map()`. Here are the implementations:
With the exception of `toLowerCase()`, production-tested versions of all of these functions are available from Lodash/fp. You can import them like this:
import { curry, map, join, split } from 'lodash/fp';
I’m being a little lazy here. Notice that this curry isn’t technically a real curry, which would always produce a unary function. Instead, it’s a simple partial application. See “What’s the Difference Between Curry and Partial Application?”, but for the purposes of this demonstration, it will work interchangeably with a real curry function.
Going back to our `toSlug()` implementation, there’s something that really bothers me about it:
That looks like a lot of nesting to me, and it’s a bit confusing to read. We can flatten the nesting with a function that will compose these functions for us automatically, meaning that it will take the output from one function and automatically patch it to the input of the next function until it spits out the final value.
Come to think of it, we have an array extras utility that sounds like it does something like that. It takes a list of values and applies a function to each of those values, accumulating a single result. The values themselves can be functions. The function is called `reduce()`, but to match the compose behavior above, we need it to reduce right to left, instead of left to right.
Good thing there’s a `reduceRight()` that does exactly what we’re looking for:
Like `.reduce()`, the array `.reduceRight()` method takes a reducer function and an initial value (`x`). We iterate over the array functions (from right to left), applying each in turn to the accumulated value (`v`).
With compose, we can rewrite our composition above without the nesting:
Of course, `compose()` comes with lodash/fp as well:
import { compose } from'lodash/fp';
Or:
const compose = require('lodash/fp/compose');
Compose is great when you’re thinking in terms of the mathematical form of composition, inside out… but what if you want to think in terms of the sequence from left to right?
There’s another form commonly called `pipe()`. Lodash calls it `flow()`:
Notice the implementation is exactly the same as `compose()`, except that we’re using `.reduce()` instead of `.reduceRight()`, which reduces left to right instead of right to left.
Let’s look at our `toSlug()` function implemented with `pipe()`:
For me, this is much easier to read.
Hardcore functional programmers define their entire application in terms of function compositions. I use it frequently to eliminate the need for temporary variables. Look at the `pipe()` version of `toSlug()` carefully and you might notice something special.
In imperative programming, when you’re performing transformations on some variable, you’ll find references to the variable in each step of the transformation. The `pipe()` implementation above is written in a points-free style, which means that it does not identify the arguments on which it operates at all.
I frequently use pipes in things like unit tests and Redux state reducers to eliminate the need for intermediary variables which exist only to hold transient values between one operation and the next.
That may sound weird at first, but as you get practice with it, you’ll find that in functional programming, you’re working with very abstract, generalized functions in which the names of things don’t matter so much. Names just get in the way. You may start to think of variables as unnecessary boilerplate.
That said, I’m of the opinion that points-free style can be taken too far. It can become too dense, and harder to understand, but if you get confused, here’s a little tip… you can tap into the flow to trace what’s going on:
Here’s how you use it:
`trace()` is just a special form of the more general `tap()`, which lets you perform some action for each value that flows through the pipe. Get it? Pipe? Tap? You can write `tap()` like this:
Now you can see how `trace()` is just a special-cased `tap()`:
You should be starting to get a sense of what functional programming is like, and how partial application & currying collaborate with function composition to help you write programs which are more readable with less boilerplate.
Eric Elliott is the author of “Programming JavaScript Applications” (O’Reilly), and advanced JavaScript and dev leadership curriculum. He has contributed to software experiences for Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica, and many more.
He works anywhere he wants with the most beautiful woman in the world.