@7urtle/lambda
JavaScript functional programming advantages

Learning functional programming improves all of your programming skills. It teaches you about general best practices for programming like inversion of control or the single responsibility principle. It also helps you write shorter, more reusable, and performant code. Not to mention that for many developers writing in a functional style is just more fun.

Functional programming is not a new thing. It is a declarative programming paradigm based on lambda calculus from 1930s. In 1937, Alan Turing himself proved that lambda calculus and Turing machines are equivalent computational systems. You don't need to learn any of the underlying mathematics but the math is there to make your coding easier. First functional programming language, LISP, was developed in 1950s. You can read about how LISP influenced early Yahoo. 70 years later, functional programming principles made their way into all major imperative object-oriented languages including Java, C#, or Python.

JavaScript through its support of first-class functions is ideal for you to take advantage of functional programming.

Improve your programming skills

It is important to know programming best practice to develop robust solution that will survive into the future. Following best practices has always required continuous study of programming as well discipline to apply in your code. Functional programming is different in a sense that it is designed to lead you towards those practices without you having to always be conscious and disciplined about it.

You might be surprised how effortless it becomes to shorter and faster code that is easy to understand and test.

Enjoy high-speed performance

Well designed functional code can be surprisingly easy to make super performant. We are building code based on pure functions. Pure functions always provide the same output for the same input. Composition is just many of these functions in a line and therefore the input at the beginning again returns the same output.

You can use this functional purity by leveraging caching mechanisms for memoization. Basically you map input of complex functions directly to their output skipping all the computation. With @7urtle/lambda it is super simple thanks to its function memo:

1
import {memo} from '@7urtle/lambda';
2
3
const complexCompose = compose(lots of pure functions);
4
5
const memoCompose = memo(complexCompose);
6
7
memoCompose('input'); // executed once for the input
8
memoCompose('input'); // not executed, returns result directly

The same function memo will be also used in the functional programming example in the next section.

Write shorter code

In experience of functional programmers, functionally written code is generally much shorter. In JavaScript that's of course supported by a shorter syntax for arrow functions, but the more important difference is cause by better reusability and modularity of the code. In short, functional programming makes your code more clever and shorter.

For an example consider this advanced functional code:

1
import {Maybe, liftA3, memo} from '@7urtle/lambda';
2
3
const getTargetOffsetTopMaybe = memo(input => Maybe.of(document.querySelector(input)).map(a => a.offsetTop));
4
const getClientHeightMaybe = memo(input => Maybe.of(document.querySelector(input)).map(a => a.clientHeight));
5
const getPositionMaybe = memo(input =>
6
liftA3
7
(TargetOffsetTop => ArticleOffsetTop => MainMenuClientHeight => TargetOffsetTop + ArticleOffsetTop - MainMenuClientHeight - 28)
8
(getTargetOffsetTopMaybe(input))
9
(getTargetOffsetTopMaybe('article'))
10
(getClientHeightMaybe('.MainMenu')));
11
12
const MaybePosition = getPositionMaybe('#someElement');
13
MaybePosition.isNothing() ? 'one of the elements may not exist' : 'position is ' + MaybePosition.value;

These 3 functions on 8 lines of code read the positions and sized of DOM elements on a page to calculate a position that would be later used for smooth scrolling animation. We are using monads Maybe because DOM represents a side effect and those elements simply may not exist. We use these monads as applicatives in liftA3 to safely calculate the position returning another Maybe monad. In the whole code that executes the animation we would not need to use any conditions or error handling because everything is elegantly taken care of by the magic of functional programming. As a bonus , because we don't want repeated calculation, the memo function takes care of caching, so each unique element is calculated only once.

Imperative code doing the same thing using more than three times as many lines to define only getPosition would look like this:

1
let ArticleOffsetTop = 0;
2
let MainMenuClientHeight = 0;
3
let cache = {};
4
5
const getPosition = function (input) {
6
if (input in cache) {
7
return cache[input];
8
}
9
10
const target = document.querySelector(input);
11
12
if(ArticleOffsetTop === 0) {
13
const article = document.querySelector('article');
14
if(article !== undefined) {
15
ArticleOffsetTop = article.offsetTop;
16
}
17
}
18
19
if(MainMenuClientHeight === 0) {
20
mainMenu = document.querySelector('.MainMenu');
21
if(mainMenu !== undefined) {
22
MainMenuClientHeight = mainMenu.clientHeight;
23
}
24
}
25
26
if(target !== undefined && ArticleOffsetTop !== 0 && MainMenuClientHeight !== 0) {
27
const TargetOffsetTop = target.offsetTop;
28
const result = TargetOffsetTop + ArticleOffsetTop - MainMenuClientHeight - 28;
29
cache[input] = result;
30
return result;
31
} else {
32
cache[input] = undefined;
33
return undefined;
34
}
35
};
36
37
const position = getPosition('#someElement');
38
39
if(position === undefined) {
40
'one of the elements may not exist';
41
} else {
42
'position is ' + position;
43
}

KISS functional programming

KISS means Keep It Simple Stupid and it is a principle that says that simple code design should be always preferred. It is similar to the single responsibility principle that states: Every function should have responsibility over a single part of the functionality. One function should do one thing.

Functional programming helps you to write easily testable and highly reusable functions. It breaks your code into small simple pieces that you can compose into more and more powerful pieces. Because of that you will be able to understand your colleagues' as well as your own code more easily. At the same time it will be effortless to painlessly build test coverage.

If you goal was to write a function that always trims its input and turns it into upper case shouting, you would likely write it like this in imperative code:

1
const shout = function (input) {
2
input = input.trim().toUpperCase();
3
const lastLetter = input.substr(-1);
4
5
if ('.?!,'.includes(lastLetter)) {
6
input = input.substr(0, input.length -1) + '!';
7
} else {
8
input = input + '!';
9
}
10
11
return input;
12
};
13
14
shout(" Don't forget to feed the turtle."); // => DON'T FORGET TO FEED THE TURTLE!

It is natural to focus on the overall task which is to build a function that does what we want and build a full function that delivers on the task. It takes discipline then to realize how to break the function down into its single responsibility parts.

This is also a reason why defensive programming is asking you to write a test for every step of your code. Large functions and classes have a tendency to hide a lot of internal logic if your unit test focuses only on a simple test of their output.

Now let's write the same function using @7urtle/lambda and functional programming principles of function composition.

1
import {trim, upperCaseOf, lengthOf, lastLetterOf, includes, compose} from '@7urtle/lambda';
2
3
const endsWithPunctuation = input => includes(lastLetterOf(input))('.?,!');
4
const replacePunctuationWithExclamation = input => substr(lengthOf(input) - 1)(0)(input) + '!';
5
const addExclamationMark = input => endsWithPunctuation(input) ? replacePunctuationWithExclamation(input) : input + '!';
6
7
const shout = compose(addExclamationMark, upperCaseOf, trim);
8
9
shout(" Don't forget to feed the turtle."); // => DON'T FORGET TO FEED THE TURTLE!

The functional code follows the principle of single responsibility and we end up with four easily testable and highly reusable functions endsWithPunctuation, replacePunctuationWithExclamation, addExclamationMark, and shout. When you look at the function shout, it is easy to deduce that the function return trimmed upper-case of its input with an exclamation mark. In the original code you have to read to whole function to understand what it does.

Inversion of control and dependency injection

In programming, it is easy to build code with tight dependencies and a mix of concerns that lead to solutions that are hard to cover with test and debug. Functional programming helps you avoid that.

Commonly you would load a module in your function or class to implement your functionality. Inversion of control turns that around so that your functions are designed to be executed in other modules. Dependency injection is a type of inversion of control. Dependency is an external code that your function is dependent on, and injection is the act of providing it to your function.

An example of a class without dependency injection would be:

1
import {Database} from 'fictional-database';
2
3
class YourClass {
4
constructor () {
5
this.database = new Database().connect();
6
}
7
yourFunction () {
8
this.database.query();
9
}
10
}

Notice that database object instance is created withing the constructor. The version with dependency injection would look like this:

1
class MyClass {
2
constructor (database) {
3
this.database = database.connect();
4
}
5
myFunction () {
6
return this.database.query();
7
}
8
}

In this example, no database module is imported and no database instance is created. Instead MyClass expects you to pass the database instance as a constructor argument. This approach makes managing dependencies as well as testing much easier.

In functional programming, this is solved by the use of pure functions. Pure function is a function that depends only on its input without any side effects:

1
let sideEffect = 1;
2
3
// impure example dependent on sideEffect
4
const impure = () => sideEffect + 1;
5
6
// pure example without any dependencies
7
const pure = input => input + 1;

Side effects that work with database are then managed through monads to create pure functions. This is a simplified example:

1
import {SyncEffect, flatMap} from '@7urtle/lambda';
2
3
// pureGetUsers :: SyncEffect(Database) -> SyncEffect([a])
4
const pureGetUsers = flatMap(database => SyncEffect.of(database.query('SELECT * FROM users;')));
5
6
// Jest test
7
test('pureGetUsers returns list of users by calling database query from SyncEffect.', () => {
8
const DatabaseMock = SyncEffect.of(() => {query: () => ['user']})
9
expect(pureGetUsers(DatabaseMock).trigger()).toEqual(['user']);
10
});

Read more about testing in JavaScript with function programming.

Enjoy programming more

There are many stories of developers that have discovered functional programming and now they would never go back. Functional programming is fun because it allows you to focus on creating clever and elegant code that is easy to reason about.

Ask For Help

If you have any questions or need any help, please leave a message on @7urtle/lambda GitHub Issues to get quick assistance. If you find any errors in @7urtle/lambda or on the website please reach out.

Get Started

Install @7urtle/lambda with NPM or add it directly to your website.