Functional JS #7: Point-free style
Now that we understand how function composition works, we will take a break from exploring foundational concepts of functional programming and how they work. Instead, we will briefly discuss point-free programming (also called: “tacit programming”) — a style of programming that is related to FP.
Even though using this style is not essential to writing code in a functional way, understanding jargon like this is useful in discussions and when trying to understand someone else’s code.
Point-free style is related to the way we invoke functions and can play nicely with curried functions. It helps us write code that is more concise and readable. As with a lot of techniques, it can of course be harmful when taken to the extreme.
Let’s jump right in! Before we understand what point-free means, it seems fitting to explain…
You can think of points as “function arguments that are explicitly referred to”. Consider this code:
In this example, the
user variable in line 8 is a point. We create an inline function, name its parameter
user, and use it when invoking
getUserDisplayName just a few characters later. We name and reference
user explicitly, so
user is a point.
That’s all there is to know, right here. Explaining why points are called points would involve a lot of mathematical intricacies, so we’ll skip this.
Does our discussion of a “point-free” style mean that points are bad and we need to get rid of them? No, not necessarily. The thing is, some functions may benefit (as far as readability is concerned) from “hiding” the argument reference from plain sight. Let’s see how.
To write the above piece of code in a point-free style, we need to get rid of the explicit reference to
user in line 8. We can see that the inline function there accepts one argument and passes it, unchanged, to
getUserDisplayName . This inline function is a trivial wrapper around
getUserDisplayName , and is functionally equivalent to it.
We can then get rid of the wrapper function, therefore saving ourselves a few keystrokes, and an explicit reference to an argument:
Take a second to understand why this still works the same way.
Does this make for a cleaner code? I would argue that it does, yes. Another upside of this, point-free, version is that we have one less variable to find a name for!
Watch out though!
In this case, rewriting code to a point-free style was as simple as removing, symmetrically, the wrapping function definition and the inner function invocation.
It is not always that easy — sometimes to go point-free, we need to transform the inner function so that its signature matches the outer function. This sometimes requires us to use more complex techniques. Among those are a couple of useful higher-order functions that we can find in most FP libraries:
unary– to wrap non-unary functions and ignore arguments other than the first one. Let's consider an example:
prop– to remove points from code that uses the attribute access operator (
pipe– to transform a function containing multiple expressions and assignments into a point-free version (we have discussed
composeearlier). An example illustrating this:
This list is by no means exhaustive. There are a lot of higher-order functions that can help us reshape functions so that they can be used without explicit references to arguments. The more utilities we use, though, and the more exotic they are — the less likely we are to produce readable code. Please keep this in mind.
In the examples above, we have used additional techniques and higher-order functions, and came up with code that has fewer explicit references to function arguments. I think the effect is improved readability in all of these.
Readability may suffer
One of the obvious downsides to point-free style is that it can get us into readability issues if we are taking it to the extreme. Let’s consider an example:
There are some obvious places that could benefit from the point-free approach here:
Getting rid of the reference to
selectedUserIds should also be easy enough:
We could then take this one step further:
I would argue that this would be taking it one step too far and hurt readability. For some people it will still be readable, but the mental overhead is there for sure.
As you can see, there are no good and bad answers here — it’s a matter of keeping balance between brevity and expressiveness.
One benefit of having all arguments explicitly referred to is easier debugging. You can simply
console.log these parameters' values and get an understanding of what is happening in the system in a given line of code.
Consider this example:
If you want to check the value of
sorted after line 4, it's easy to add a
console.log or a
debugger to understand what's going on:
Now let’s consider the point-free version:
It is not clear how we would examine the sorted value (after line 4) now, without moving back to the previous version and referencing
Luckily, there is a utility function we can use — conventionally named
trace. Its definition is really simple:
We can simply drop it in the point-free version and get the same debugging experience as with the original version:
Why bother though?
If you think that all these gotchas and potentially reduced readability are not worth it to save a couple of keystrokes, let’s look at other benefits of the point-free style. With that, you will be able to make a more informed choice on what coding style to use in various circumstances:
It reads like English
Let’s compare a piece of code with its point-free equivalent:
Which one do you think reads better? It’s obviously a matter of preference, but to me — the second version reads more like English.
When used properly, not only does point-free style make for a more concise code — it also makes it more readable.
No variables to name
There are only two hard things in Computer Science: cache invalidation and naming things.
– Phil Karlton
Coming up with names for variables and functions is sometimes hard. With point-free style, you can save some of that effort.
Of course — this is only partly serious. Naming variables explicitly can sometimes help you think about abstractions and thus improve code design.
On the other hand, the mental effort is sometimes definitely there. Given that code is read many more times that it is written, not introducing unnecessary noise in the form of variables may actually be a good thing.
Point-free style is a code-style choice and it’s not essential to make your code more functional. Understanding this concept, however, makes reading and discussing others’ code easier.
As with many things in life (and especially in programming), overusing it might introduce more problems than it solves, and you need to trust your intuition (and experience) on where the balance is.
Keep it reasonable and think twice if removing an explicit variable reference makes the code easier or harder to read. Use point-free style in moderation and make your code easier to read and follow.
Next up, we will focus on the practical side of things. We’ll discuss libraries that we can incorporate in our code to make writing functional code easier and more idiomatic. Stay tuned!