Ondra Bašista
Over the last two years, we in our company (back-end written with a combination of Node.js and TypeScript) have started to look for a kind of semi-functional concept of writing a code.
In part, we were probably hit by a wave of functional hype, which I dare to say, the increasingly powerful React community began to push into the real mainstream. And in part, it simply made sense in the ecosystem of Node.js, TypeScript, GraphQL, and microservices.
I would like to point out that the vast majority of the team comes from the world of those classic enterprise technologies such as Java / Spring, typically used more in the context of OOP, DI, etc.
The term more functional style of writing code is intentionally written, because it may not be (for a conceited academic!) Strictly accurate FP such as Haskell (?). But let's say that on the imaginary curve of the functional TypeScript we can move from helper libraries (Ramda, Lodash, etc.) to a complex system of the fp-ts type.
But what does functional mean? And how do you actually define such a style of writing / thinking?
But first, a small, simplified glossary of terms:
I will try to avoid academic descriptions from Wikipedia. Every ninny from PHP to Angular knows that a monad is a monoid in the category of endofunctors :-). So I will try to share a few notes from practice (not the definition of functional programming), and avoid phrases of the type "declarative vs ...", because declarative today is simply where is what.
We think more about the structures that manipulate data than about the data as such
I will try to sketch a simple example, but it is necessary to realize that this is a bit illustrative, because the core of thinking about FP structures can be a level more abstract - in algebraic structures.
interface Washable { wash: () => ..., clean: () => ..., } interface Openable { open: () => ..., close: () => ..., } const vehicle: Washable & Openable = { wash: () => ..., clean: () => ..., open: () => ..., close: () => ..., } const table: Washable= { wash: () => ..., clean: () => ..., } const tableWithDoors: Washable & Openable = { wash: () => ..., clean: () => ..., open: () => ..., close: () => ..., } const openAndWash = (entity: Washable & Openable) = { return entity.open().then(res => res.wash()); } openAndWash(vehicle); openAndWash(tableWithDoors); // bad openAndWash(table);
As can be seen from the picture, the openAndWash function does not care whether it opens the door at the vehicle or from the table, only the reliability of the structure is important - every Washable structure is able to apply clean. Just as a functorial is able to take any data from a wrapper, apply a function to it, and repack it in an envelope. And that is the canon.
That dividing line may seem insignificant but thinking "data agnostic" makes an order of magnitude greater sense when we begin to look at a more abstract structure that does not imply the nature of the data at all but offers only different ways of manipulating another structure.
If we imagine a structure of the functorial type behind the Washable type - which, like Washable, is able to manipulate data through the expected pattern, we get a little closer to the poodle's core.
We will talk about algebraic structures (functorials, monads) and more complex types in a future blog. However, the message is as follows: a functionally tuned programmer thinks more about the relationship between the structures that manipulate the data than about the data.
We chain, we chain!
const cleanMyCar = <A>(car:A) => pipe( car, openDoor, cleanDoor, closeDoor, cleanWindow, (car) => `${car} is now clean!` ) const cleanMyCar = <A>(car:A) => flow( openDoor, cleanDoor, closeDoor, cleanWindow, (car) => (`${car} is now clean!`) )(car)
The picture is quite descriptive - if something (not only) is visually typical for FP, it is the chaining of functions into a gradually connected whole or the composition of simpler functions into more complex ones.
The use of different pipes may seem interchangeable with the classic declaration of variables, but I have noticed that it leads (guides, does not directly imply) the following:
The ancient mantra certainly does not apply only to FP, but repetition is the mother of wisdom. We fold, intersect, take, do not inherit. More complex functions are created by the composition of independently testable simpler units. We do not createdivine monoliths, but variable pieces of a kit.
const postService = <A,B,C>() => ({ ...withSendMessage, ...withCreateMessage, ...withReadMessage, ...withListItems, sayHello: () => 'hello! :-)' })
From my point of view, I have highlighted three key points for thinking about a more functional style of programming, without deliberately honing directly into the more complex world of higher-kinded types and implementations of structures such as monads.
However, the depths of TypeScript are much more colourful than it might seem at first glance, and in the next articles we will talk about tools that allow such programming at a level often more complex than for languages proclaimed as functional (e.g. Elm without HKT support).
Functional JavaScript does not have to be just lambda, map, reduce and filter. So sometimes next time about FP-TS, higher-kinded types, functorials, monads and type classes, side effects, error handling and asynchronous programming, because that's where the proverbial "Eureka!" Is somewhere.
P.S. The author does not write messages in the refrigerator in Haskell and reserves the right to slightly distort mathematical concepts, etc.
Just leave us a message using the contact form or contact our sales department directly. We will arrange a meeting and discuss your business needs. Together we will discuss the options and propose the most suitable solution.