Pointfree Style Programming
Do you wish to write more readable and easier to understand JavaScript code? Pointfree style will help you write better code, by letting you and future maintainers focus on functions and their meanings, rather than on minor details. In this article, we’ll focus on this style of programming, and its advantages — as well as its possible disadvantages! Thus, this article stays in the trend of applying functional programming techniques to enhance your code, as in our previous work on chaining and fluent interfaces.
What is Pointfree Style?
Pointfree style has to do with not needing to specify the arguments for each function application. The term “point” stands for a function parameter, and pointfree has to do with not naming such parameters. In this paradigm we want the reader to focus on what functions are, not caring about the specific names of its parameters.
Pointfree style is also known as tacit programming by its champions, or as Pointless Programming by its detractors!
This style is most often seen when composing or pipelining functions, common patterns in Functional Programming. The idea is to work at a higher level of abstraction, focusing on what functions do, and avoiding minor details such as what parameters are called. If there’s less code to read, you’ll be able to understand it quicker… Although, if ill-used or taken to extremes, pointfree style may end up making code more obscure instead!
Let’s see an actual example so we can get a taste of pointfree style!
An example
Monsieur Jourdain, a character in Molière’s play ”Le Bourgeois Gentilhomme” (“The Bourgeois Gentleman”) was quite surprised to learn that he had been speaking in prose for more than 40 years without even knowing it. You, the reader of this series of articles, may have not noticed that we already used pointfree style before!
In the article on Chaining and Fluent Interfaces we saw code from my Mastering JavaScript Functional Programming book, where we started by defining a few (basically nonsensical) operations:
const testOdd = x => x % 2 === 1;
const testUnderFifty = x => x < 50;
const duplicate = x => x + x;
const addThree = x => x + 3;
We applied those operations sequentially to an array. First, we dropped even numbers; then, we doubled the remaining ones; after that, we dropped results that were 50 or more, and finally, we added 3 to whatever was left.
const myArray = [22, 9, 60, 24, 11, 63];
const a0 = myArray
.filter(testOdd)
.map(duplicate)
.filter(testUnderFifty)
.map(addThree);
// [ 21, 25 ]
In all cases, we didn’t specify the arguments to the functions; we used named functions instead of something like .filter(x => x % 2 === 1)
— this is pointfree style! The resulting code is more compact, and simpler to read: “keep only odd numbers, duplicate all, keep numbers under 50, finally add 3 to all”.
With pointfree style, all you see are function names, with little “extra-JS” things. This style achieves brevity, but you must endeavor to pick good, readily understandable function names. (A famous saying, “There are only two hard things in Computer Science: cache invalidation and naming things” comes to mind…) Applying this style, you will end up with two kinds of functions: short ones with a single task and a clear name, and longer ones that coordinate other functions, using pointfree style for readability.
Another example
Let’s see another example, a (slightly) more realistic one. Suppose you have to transform a title into ”title case” — or at least a simplified version of it, in which all words have their first word capitalized (in actual title case format some words don’t get their first letter capitalized unless at the start of the title). We can do this in pointfree style by using a few functions. First, let’s see a couple of standard functional programming tools: pipelining and mapping.
const pipeline = (...fns) => fns.reduce((result, f) => (...args) => f(result(...args)));
const map = fn => arr => arr.map(fn);
Pipelining (for which some proposals are being considered at this time) just applies the first function to a parameter, then passes the result to the second function, then that result to the third function, etc. As in Linux and Unix, a pipe gets some input, processes it, and passes it on to the next function in the pipeline. Our map(...)
function is a curried version of the .map(...)
method, more suitable for functional programming purposes.
Now, having these tools, let’s work on making a string in title case.
const separateStringIntoWords = (str) => str.split(" ");
const firstToUpper = (str) => str[0].toUpperCase() + str.slice(1).toLowerCase();
const joinWordsIntoString = arr => arr.join(" ");
const makeTitleCase = pipeline(separateStringIntoWords, map(firstToUpper), joinWordsIntoString);
To convert a title into title case you just use makeTitleCase(...)
as follows.
makeTitleCase("GO TO sTaTeMeNt conSIDered HARMful");
// "Go To Statement Considered Harmful"
You may write this more succinctly, even as a “one-liner” — but is it clearer?
const makeTitleCase2 = (str) => str.split(" ").map(word => word[0].toUpperCase() + word.slice(1).toLowerCase()).join(" ");
makeTitleCase2("GO TO sTaTeMeNt conSIDered HARMful");
// "Go To Statement Considered Harmful"
To understand this code, the reader must successively realize that split(…) separates the title into individual words, that map(…) transforms each word to have its first letter in uppercase, and that join(…) finally puts the words together to form a title. For reasonably proficient JavaScript developers, if this kind of work is done only in a single place in your application, this code may be fine. However, if you need splitting, uppercasing, and joining in several places, and you care for more legibility, I’d say that the earlier pointfree style version is easier to understand.
Let’s go further into this. Suppose you want to debug the title casing function. As is, makeTitleCase(...)
can be seen as correct by understanding that you apply three functions: one that (very likely!) takes a string and separates into words, then a second one that maps each word by making its first letter uppercase, and a final one that makes a string out of several words. Those auxiliary functions will probably be used elsewhere, so that’s not a waste. And, I believe that the extra clarity compensates the possibly longer code.
Open Source Session Replay
OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data.
Start enjoying your debugging experience - start using OpenReplay for free.
Problems
Despite all we’ve said before, there are some “gotchas” that may get you. A classic puzzle in JavaScript asks to explain the different results in the following calls.
const numbers = [22,9,60,12,4,56]
numbers.map(Number.parseFloat); // [22, 9, 60, 12, 4, 56]
numbers.map(Number.parseInt); // [22, NaN, NaN, 5, NaN, NaN]
Why is this happening? The key is that Number.parseFloat(…) receives a single argument —the string to parse— while Number.parseInt(…) allows a second optional argument, the radix for the number. The array.map(…) method calls whatever function you provide with three arguments; Number.parseFloat(…) ignores the two extra ones, but Number.parseInt(…) takes the second one to be a radix, and all problems ensue. In this case using an arrow function would have been correct:
numbers.map((x) => parseInt(x)); // [22, 9, 60, 12, 4, 56]
When you use functions with extra (and, as here, unwanted arguments) Functional Programming provides another solution. We could use a higher order function to turn the given function into a unary one.
const unary = fn => (...args) => fn(args[0]);
Now, our parsing problem goes away.
numbers.map(unary(Number.parseInt)); // [22, 9, 60, 12, 4, 56]
Whenever you use pointfree style with functions that you didn’t write yourself, you expose yourself to this sort of bug; be careful!
Summary
Pointfree style is a valid pattern in JavaScript programming and helps you focus on having understandable, well named, short single-objective functions, composed in previsible ways. Give it a whirl!