It is not enough for code to simply work. Good code is meant to be easy to understand, and it is much easier to understand code that is readable.
Comments Considered Harmful Traditionally, algorithms have often been explained in large comment blocks. The notion that writing many comments makes code easier to understand is still quite prevalent in the industry. Yet when it comes to code, less is very often more.
Good comments are to the point and explain the why, not the how:
This leaves us with explaining the how. One way to make code readable is to have it flow like the natural language, describe the algorithm in a higher level of abstraction. This leaves the implementation open to explore to those who desire details, yet hides it from those who simply wish to get the gist.
That is, instead of this:
insertIntoAfter(2, [1, 3], 1); //[1, 2, 3]
We have something like this:
1 insert(2).into([1, 3]).after(1); //[1, 2, 3]
The fluent version basically reads like a natural language sentence, which makes it very easy to understand. This is especially useful in functions that take more than a few arguments – the regular way devolves into a soup of obscure parameters, reminiscent of the Polish notation, while the fluent interface remains highly readable at a glance.
Fluent interfaces are nothing groundbreaking. They are, essentially, a form of domain-specific languages. In fact, most of BDD style testing libraries use some sort of fluent API. For example:
The prevalence of these types of interfaces in places where readability is extremely important (unit tests can be considered a type of documentation) is a signal that this works well in practice.
One idea of implementing fluent functions in vanilla JS relies on returning objects with a single property (that is, the next function to chain while using it) while relying on closures to hold intermediate parameters.
This is actually not too bad, but it can be a little overwhelming due to so many function and return keywords everywhere.
ES6 lets us write this in a more concise way, at the cost of introducing some crazy punctuation:
Another, slightly more limiting option is to forego function composition entirely and just go with a single parameters object and one function that can handle it all:
Implementing a library for fluency
How might a library for creating fluent interfaces look like? While there are a couple of possible approaches, the main idea behind them would be to offer some sort of builder which would produce a segmented fluent function. Let us first consider how this might be achieved in the future.
Ideal interface for fluent function creation Arguably, the ideal interface for creating fluent interfaces would itself be fluent. It should also self-document how to use the created function. In other words, it would look something like this:
Using ES6 Proxy
The Proxy object in ES6 is very promising in this regard. It is, however, not ready for any kind of real usage just yet. There is a shim called harmony-reflect, but even that at the time of writing this post is mostly supported via browser flags. In short, the Proxy object allows us to define what happens when a property is accessed, and intercept that with some custom behavior. In our case, we can simply store the property that was called as the new segment name. Here’s how that might look:
Lest we forget the present..
It seems there is no simple way to implement the ideal builder interface under ES5. There is always the possibility of transpiling (a la JSX), but that would most likely not be worth the trouble, unless creating an extraordinary amount of fluent functions.
All is not lost, however, as with some lenience in the permitted builder interface we can still arrive at something both readable and very backwards compatible.
Not perfect, but it resembles the ideal interface enough that I consider it usable. It is currently able to extend existing objects too, which would look something like this:
There’s more to be done:
Argument validation – from required and optional arguments, to some sort of rudimentary type validation, a lot can be done here to facilitate solid API design with little effort.
Browser build – the library is currently Node.js only, but it would be trivial to create a build for the browser. Since the implementation is relatively straightforward, it should support pretty much anything, even the last few remaining IE6s running on potatoes.
Branching segments – this might require altering the interface a little bit, but a nice feature would be to support alternate branches for fluent functions, with different implementations backing them. For example:
Writing clean and readable code doesn’t have to be difficult. While fluent interfaces might take a little bit of work to get right, they are an important tool in improving readability. Most importantly, clean code is supposed to be fun to both write and read. What would an entirely fluent program look like? Could it be read like prose, essentially a high level description of the problem being solved?
Experiment, and prosper.