Pointfree programming is a style of programming free of points. Great, you got it. Blog post over.
While my first sentence is true, it's pretty unhelpful, so let me explain what pointfree programming is a little bit better.
In order to explain pointfree, we first need to understand what a "point" is. I'm going to teach you by showing you, and I'll use the
Array.prototype.map method to demonstrate that.
const arr = [1, 2, 3, 4, 5] const doubles = arr.map(x => x * 2) console.log(doubles) // [2, 4, 6, 8, 10]
Alright, really simple example. We pass a function to the
map method that multiplies the number by
2 and we get a new array with doubled numbers.
I'd like to draw your attention to the function passed to
x => x * 2. What is
x? Maybe a better question, why
x? Why not
potato? Each would work equally well. What is it that
Another way of putting that is, what does
x point to?
The function we passed to
map is called a lambda, a single-use anonymous function. We use an arbitrary, interim variable to point to a value abstractly. Sure, we could follow "clean code" principles and name our interim variable better, but that would make it dependent upon the array, not the operation we're doing to the array.
Another way to put this is that using lambdas in this way puts more focus on the data, and less on the functions transforming the data. We want to flip this, and as we'll see when we get into composition, it's necessary that we make this change.
There's a way to use higher order functions that avoids lambdas, and increase legibility and reusability in our programs in the process. Let's walk through that process.
The first thing we need to understand is that we can pass in the name of a function to
map, we don't need to use a lambda. Our code can look like this:
const arr = [1, 2, 3, 4, 5] const double = x => x * 2 const doubles = arr.map(double)
We have pulled our lambda out of the
map call and saved it in a variable as a function expression. We can now pass this variable directly to
map. This is pointfree programming. We no longer concern ourselves with the point in our higher order functions. Instead we make a simple, easily unit-tested (though you probably don't need to) function, and pass that in by name.
const arr = [1, 2, 3, 4, 5] const multiply = x => y => x * y const multiplyBy2 = multiply(2) const doubles = arr.map(multiplyBy2)
This time, we made a generic curried
multiply function. We apply the first argument to
multiply to create our
multiplyBy2 function which is identical to our
double function from before. But this time, I can pass
multiplyBy2 around my application for reusability.
"But Kyle, multiplying by 2 is really easy. Give me something with some more substance."
I hear you. Let's take a more complicated example and show why pulling the function out might be helpful. What if I want to slug-ify a list of strings? We can do that kinda functionally and using pointfree programming.
The steps in this algorithm are pretty simple: lower case the string and replace spaces with hyphens. Let's create a bunch of random strings and give it a try.
const strings = [ 'Portland summers are amazing', 'I like big beards and I cannot lie', 'I am out of ideas for strings', // ha ] const slugs = strings.map(str => str .toLowerCase() .split(' ') .join('-') ) console.log(slugs) // [ // "portland-summers-are-amazing", // "i-like-big-beards-and-i-cannot-lie", // "i-am-out-of-ideas-for-strings" // ]
Let's make this pointfree and break up the steps of our algorithm.
// Same strings as before const lowerCase = str => str.toLowerCase() const split = separator => str => str.split(separator) const join = separator => arr => arr.join(separator) const splitAtSpace = split(' ') const joinWithHyphen = join('-') // Bit of a crazy way to do it, but should still make sense const slugs = strings .map(lowerCase) .map(splitAtSpace) .map(joinWithHyphen)
Now, I can read in plain English what's going on in the final part of my program. Admittedly, it's pretty verbose this way, so let me give you a sneak peek to the next blog post and do this magically with composition:
const slugify = compose(joinWithHyphen, splitAtSpace, lowerCase) const slugs = strings.map(slugify)
Boom! 💥 We're using pointfree all over the place and creating a brand new
slugify function in the process. I'll explain this in greater detail in the next post, so stay tuned.
Pointfree programming gives us a few advantages. It encourages us to write small, easily tested, reusable functions to use throughout our applications. It encourages us to create well named functions so our programs become legible combinations of functions. And lastly, it reduces the surface area of bugs by eliminating lambdas and interim variables.
It will take some time to get used to, but with practice, reading pointfree programming will become as natural as reading the imperative pointful programming you're probably doing today. Give it a try in practice, see if there are places you find it useful!
I write a newsletter to share my thoughts and the projects I'm working on. I would love for you to join the conversation. You can unsubscribe at any time.