How point-free composition will make you a better functional programmer

Cristian Salcescu
May 14, 2018 · 6 min read

Learn functional React, in a project-based way, with Functional Architecture with React and Redux.

Photo by Hans-Peter Gauster on Unsplash

Point-free style — aims to reduce some of the visual clutter by removing unnecessary parameter-argument mapping.

Consider the flowing code:

let newBooks = books.filter(point => isTechnology(point))

Now look at the same code after eliminating points (parameters/arguments):

let newBooks = books.filter(isTechnology)

Point-free in List Operations

Let’s do list operations in a point-free style.

Say we need to find the technology titles in a list of books, prepare the book object with all information for the view, and sort the books by the author’s name.

Here is how the code would look:

function getBooks(){
return books.filter(isTechnology)
.map(toBookView)
.sort(ascByAuthor);

}
//Small functions with points
function isTechnology(book){
return book.type === "T";
}
function toBookView(book){
return Object.freeze({
title : book.title,
author : authors[book.authorID].name
});
}

function ascByAuthor(book1, book2){
if(book1.author < book2.author) return -1;
if(book1.author > book2.author) return 1;
return 0;
}

The callbacks isTechnology(), toBookView(), ascByAuthor() are small functions with intention-revealing names. They are not built in a point-free style.

The code assembling all these functions in getBooks() is point-free.

Our natural way of dealing with a problem is to break it into smaller pieces and then put everything back together.

We break the bigger task up into several functions doing smaller tasks. Then we re-combine these smaller functions to solve the initial problem.

Let’s read the requirements again:

We need to find the technology titles in a list of books, prepare the book object with all information for the view, and sort the books by the author’s name.

We created:

  • isTechnology() predicate to check if it’s a technology book
  • toViewBook() to build an object with all the information for the view
  • ascByAuthorname() to sort two books ascending by the author’s name
  • getBooks() to combine all these small functions together in a point-free style
function getBooks(){
return books.filter(isTechnology)
.map(toBookView)
.sort(ascByAuthor);

}

Steps towards point-free composition

There is no additional anonymous callback when doing point-free composition. No function keyword, no arrow syntax => . All we see are function names.

  • In most cases, extract out the callbacks in named functions.
  • In simple cases, just use an utility function from the toolbox to create the callback on the fly. Look at the prop() function, for example.
  • Write the coordinator function in a point-free style.

The consequence of writing code this way is a lot of small functions with intention revealing names. Naming these small functions requires time, but if it’s done well, it will make the code easier to read.

There will be two kinds of functions:

  • Functions doing one task: they are pure or closure functions. Usually they are not built in a point-free style, but instead have good names.
  • Functions coordinating a lot of tasks: joining these small tasks in a point-free style makes it easier to read.

I’m not aiming at having everything point-free. I’m aiming for point-free in specific places, especially when composing functions.

Partial application

Partial application refers to the process of fixing a number of arguments of a function. — source

Let’s take the case of a function isOfType() that can filter by any book type. Look at the code below:

let newBooks = books.filter(book => isBookOfType("T", book));

For point-free composition, I refactor out to a named function and then compose all of it back.

function isTechnology(book){
return isBookOfType("T", book);
}
let newBooks = books.filter(isTechnology);

In this case, we can improve more and create the new predicate function in a point-free style using partial application.

let isTechnology = isBookOfType.bind(null, "T");
let newBooks = books.filter(isTechnology);

A point-free function by definition doesn’t use the function keyword or the => symbol.

A predicate function is a function that takes one item as input and returns true/false based on whether the item satisfies a condition. — source

Point-free with Promises

Let’s take the case of retrieving a set of todos from the server, selecting the most important, and then rendering them on the screen.

Below is the code:

fetchTodos().then((todos) => {
let topTodos = getTopPriority(todos);
render(topTodos);
}).catch((error) => {
console.error("There was an error :" + error.status);
});
function getTopPriority(todos){}function render(todos){}

Now let’s refactor it to a point-free style. There is something different this time. As you can see, the result of the getTopPriority() function is used as input for the render() function. We can use the promise chaining system to combine together the getTopPriority() and render() functions.

As we did before, we refactor out the catch callback to a named function.

fetchTodos()
.then(getTopPriority)
.then(render)
.catch(handleError);
function handleError(error){
console.error("There was an error :" + error.status);
}

Each then() can return a value that it has used as the input value for the next then() call.

Toolbox

In point-free style, we need a toolbox, a set of functions to use over and over again in common scenarios.

I’ll take the case of sending a piece of data through multiple transformations.

A person object with firstName and lastName will first have both names capitalized. Then the fullName will be computed. Then at the end, the full name will be shortened to fit the screen constraints.

Below is the code showing all these steps:

function toFullNameForView(person){
let newPerson = capitalizeNames(person);
let fullName = computeFullName(newPerson);
return shorterName(fullName);
}
function capitalizeNames(person){
return {
firstName : capitalize(person.firstName),
lastName : capitalize(person.lastName)
}
}
function capitalize(name) {
return name.charAt(0).toUpperCase() + name.slice(1);
}
function computeFullName(person){
return person.firstName + " " + person.lastName;
}
function shorterName(name){
return name.substring(0, 8);
}

capitalizeNames(), computeFullName(), shorterName() are the small pure functions. toFullNameForView() is the coordinator function, which composes them together.

Defining the toFullNameForView() in point-free style will require a new utility function: pipe().

function pipe(...functions){ 
return function(value){
return functions.reduce(composeLeftToRight, value);
}
}
function composeLeftToRight(computedValue, fn){
return fn(computedValue);
}

pipe() chains functions together so that the output of each function is used as the input for the next one.

Now let’s define the coordinator function in point-free style.

let toFullNameForView = pipe(
capitalizeNames,
computeFullName,
shorterName,
);

Let’s take a simple case like the following:

books.map((book) => book.title);

One point-free option is to refactor out the callback to a named function:

let titles = books.map(toBookTitle);function toBookTitle(book){
return book.title;
}

Another option is to use a general purpose function that can retrieve a property : prop() .

let titles = books.map(prop("title"));function prop(propName){
return function getProp(obj){
return obj[propName];
}
}

unary() is a function decorator that takes a function and returns a function taking one parameter. It’s usually used to fix problems when the function is called with more arguments than necessary.

function unary(fn){
return function unaryDecorator(first){
return fn.call(this, first);
}
}

Let’s use a console.log() in a point-free style to log all numbers from an array.

let numbers = [1,2,3,4,5,6];
numbers.forEach(console.log);
//1 0 (6) [1, 2, 3, 4, 5, 6]
//2 1 (6) [1, 2, 3, 4, 5, 6]
//...

As you can see, there is a problem. console.log() receives more arguments that it needs. We have to make sure it’s called with one argument only.

Here is the code using the unary() function.

numbers.forEach(unary(console.log));
//1 2 3 4 5 6

Below is another example using parseInt() in a point-free style:

let numbers = [1,2,3,4,5,6];numbers.map(parseInt); //[1, NaN, NaN, NaN, NaN, NaN]
numbers.map(unary(parseInt)); //[1, 2, 3, 4, 5, 6]

Point-free with Methods

Let’s now use methods in a point-free style. I create an object using a factory function and then use the object’s method in a point-free style.

Here is the example:

function Service() {
let url = "http://";

function reload(){
console.log(url);
}

return Object.freeze({
reload
});
}
var service = new Service();
setTimeout(service.reload); //http:

Using the method in a point-free style works great.

Next I’ll do the same thing, but this time I build the object using class.

class Service {
constructor(){
this.url = "http:";
}

reload(){
console.log(this.url);
}
}
var service = new Service();setTimeout(() => service.reload(), 0); //http:
setTimeout(service.reload, 0); //undefined

As you can see, this lost context.

The problem can be solved using bind() for example.

setTimeout(service.reload.bind(service), 0);

This is one of the reasons I favor factory functions over classes. For a more in-depth comparison, take a look at Class vs Factory function: exploring the way forward.

Conclusion

Point-free composition improves the clarity and readability of the code.

More than that, it favors the practice of decomposing everything into smaller pieces that can be then composed together in a very expressive manner.

Point-free style goes hand in hand with the practice of giving intention-revealing names. Taking the time to write good function names makes point-free composition much easier to read.

Discover Functional JavaScript was named one of the best new Functional Programming books by BookAuthority!

For more on applying functional programming techniques in React take a look at Functional React.

Learn functional React, in a project-based way, with Functional Architecture with React and Redux.

You can find me also on Twitter.

Cristian Salcescu

Written by

Author of Discover Functional JavaScript and Functional React. Enthusiastic about sharing ideas.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade