Heraclitus said that everything flows. The reactive paradigm followers like to say that everything is a stream. The phrase attributed to Heraclitus is valid for both functional and reactive paradigms. In the first one, we have pipelines, in the second one - streams. That testifies to the significant similarity between these paradigms and shows that reactive programming (but more FRP) is, in fact, an asynchronous implementation of a functional flow.
There are many studies on the subject of functional programming. However, finding noteworthy resources about naming or specific inspiring conventions is quite a challenge. It requires a healthy dose of creativity and often turns out to be far more complicated than it might seem at first glance.
How to standardize descriptive names?
In essence, I’ve observed that many programmers have a problem with descriptive names. The style guide that my colleagues from Straal and I have created is a fulfillment of our experience and expectations, presenting a ready set of good practices and restrictions in our projects. It makes our code reviews much easier and effectively tackles issues such as naming dilemmas.
The article that you read is an analysis of our style guide, and you can find it on our GitHub repository. Besides, we apply strict ESLint rules, which are complementary to this style guide.
Why “Lexical functional”?
The declarative composition is essential for understanding what FP is, but we’ll go one step ahead and think about the naming structure in our functions.
The inspiration for the title is “Lexical functional grammar”, which provides a specific grammar framework to focus on the syntax of multiple dimensions of the linguistic structure.
“Functional” is something more than just a common phrase — it’s a connection of both ideas to focus on specific problems for code writing. In this article, you will see the jargon and structures to the naming convention in the FP-inspired world. Sometimes I use specific FP-terms in brackets to provoke your curiosity or wink knowingly at those already familiar with them.
It’s worth pointing out that constants are the strictest declaration. This is the first step in avoiding mutations because using other variable declaration can result in side effects appearing quickly.
For boolean values describing a state of something we mostly use the following convention:
As you see we have the following scheme for the functions with a specific role (I describe this part in detail in further sections):
Why do we use const operators in this case? Because it provides the temporal dead zone and helps to take care of a correct order in the code.
Operations and operator functions
The above scheme could be a base for most of your problems in a naming dilemma. As you see, our composition includes target collection (also known as a functor because it is mappable), reducer (an operator function), and optional argument — in this case “accumulator”.
Let’s start from “reduce” — the word is a verb which indicates an action, so it is an operation — a declaration with defined scopes per argument with all the information on what that function does.
We also have an operator called “reducer” (or “iteratee”) — a noun, which means it’s a role that provides functionality to realize “reduce” operation, we called it an operator function.
“Reduced” is an adjective and indicates that something has been completed — it is a result of our process.
Let’s check implementations of reduction in Lodash and vanilla JS — as you can see, that that terminology is nothing innovative and you have probably seen it before. Now it’s time to use it more consciously and look for analogies in your code.
For a more “lexical” way, we can use it in our code as a suffix — then you always know what the role of each of the individual variables is:
In the sort functions (“union”, “sort”), the invoke function per element is called a comparator. For “filter”, “some”, “find”, and similar operations (using probably pure functions) we use predicate function called checker or just predicate:
In the next part of the article, you can see we use that convention totally in all cases.
Pipelines are a handy thing to a flow between functions. It’s an essence of functional programming.
Pipe (or compose in the opposite direction) as a functional pipeline:
Pipe operator in a reactive stream:
Both ways - reactive and functional use the same concept of a pipe. The main difference is, of course, the approach to synchronicity.
Curry and partial
Forget about “this”, “apply”, and “call” methods. Everything you need is a flat description of flow and proper declarations that express your control flow with the partial transmission of arguments:
We have extended the example of reducing called “curriedReduce” (we can also call it “reduction”) — it means that you have to execute that function to end the process. Pay attention to vocabulary — call your curried functions “curriedSomething” or as an ongoing process (like in this case, “reduction”).
We can also create curried setters with predefined arguments or change the order of execution and use partial. The result of the implementation of both functions will be the same:
For a simple merging, “spread operators” will be enough. For more involved, you should remember about avoiding mutations on your reference, use Lenses, or immutable library like Immutable.js.
The analogic situation with getter functions:
Instead of them, you can use conditional (ternary) operators and cond functions. That increases clarity, and helps to avoid imperative chaos in your code.
Conditional (ternary) operators — the condition can be a predicate (returns true or false) or boolean (e.g. hasFoo):
A better choice for more complicated cases:
We have three typical use cases: dictionaries, switches, and factories.
We use dictionaries just like in other languages to keep key/values:
In most cases, we can replace typical JS Switch (with break/return) by cond operation function. But in this case, any conditional will be executed if none of them comes true. In some extreme situations, getting a specific function by a key value is a better choice. The following code shows behaviour similar to a classical switch.
We use neither classes nor constructors. Factories are functions that create objects of any kind— singleton or instances. The term “middleware” is used to define the factory function with partial arguments, e.g., initial options. Our implementation of Factory is quite specific — we don’t use
this (to use more explicit contexts and for more FP-friendly style), so you can’t chain methods, but you can use the pipeline:
It’s customary to use a “$” suffix in streams. We distinguish two types of observables: hot and cold — everything will be more apparent with the rules below.
Hot observables are independent of subscribers — it can emit some changes without an observer. In this case, we use suffix ‘Dispatcher’ because we use them like a singleton — for one event, we have only one declaration on the whole code and reuse it by importing:
In any other case, the observable will be cold:
For functions that create a new observable:
I know that the topic has not been exhausted, yet hope that the analysis of tools and approaches will be helpful and useful for you. I hope that you enjoy the presented proposition of a solution for a common problem of many teams — a naming inconsistency in code. If you have any ideas for improvements, feel free to leave me a comment.
If you have seen that you can improve some of this, I’m waiting for your comment, or you can think about your own “lexical guide”.
Spread the word!
For more information about FP I can recommend these lectures: