May 01, 2019

Just Enough FP: Pointfree

edit

Pointfree programming is a style of programming free of points. Great, you got it. Blog post over.

Just kidding!

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.

The map method is a higher order function (because it takes a function as an argument). You can read the docs here. Let’s create a simple example:

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 map, x => x * 2. What is x? Maybe a better question, why x? Why not foo, or num, or potato? Each would work equally well. What is it that x signifies?

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.

Now, let’s take it a step further. Let’s combine currying and partial application to make this a little more functional.

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.

Conclusion

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!


In this series, I’m going over material from my Just Enough Functional Programming in JavaScript course on egghead. This post is based on the lesson Eliminate Anonymous JavaScript Functions with Pointfree Programming.


Liked the post?
Give the author a dopamine boost with a few "beard strokes". Click the beard up to 50 times to show your appreciation.
Kyle Shevlin's face, which is mostly a beard with eyes

Kyle Shevlin is the founder & lead software engineer of Agathist, a software development firm with a mission to build good software with good people.

Logo for Introduction to State Machines and XState
Introduction to State Machines and XState
Check out my courses!
If you enjoy my posts, you might enjoy my courses, too. Click the button to view the course or go to Courses for more information.
Sign up for my newsletter
Let's chat some more about TypeScript, React, and frontend web development. Unsubscribe at any time.