Functional Programming Introduction with JavaScript

Jose Emmanuel Tabares Sotelo
Frontend at Accenture
10 min readApr 30, 2024

Introduction

JavaScript was born as functional programming language, back in 1995; Netscape Communications, the creators of the Netscape Browser realized that their browser needed a scripting language embedded in HTML, then Brendan Eich enters to the scene and created a functional programming language combined with other paradigms to complete the requirements, hence we are involved in this trend until today even if we don’t have any clue about functional programming.

Right now, ES6 oversees supporting functional programming and object-oriented programming parading (sometimes partially), we can say that JavaScript is functional. However real functional programming languages (providing complete functional programming features) are supported in industry by: Common Lisp, Scheme, Clojure, Wolfram Language, Racket, Erlang, Elixir, OCaml, Haskell, F#. Historically it been less popular than imperative and object-oriented programming.

Top paying technologies

With this information in mind, I recall when I started to work with JavaScript (primarily with JQuery) I was using callbacks and scripting code without any clear pattern in my bag, when Angular was born in 2016 I started to use JavaScript in a serious way, always learned as Object-Oriented language and nothing else. However, the applications started to increase the complexity and computation on the browser side, this was reflected in the code and the increasing challenges, starting a new perspective from web pages to web applications.

The requirements started to grow beside the frameworks and libraries, then I learned Redux (NgRx) in Angular to improve the data flow using the state of the application; using state management. But this archaic creature looks very familiar to object-oriented (actions and reducers were classes), I assumed was a subproduct of this paradigm, but new concepts are in the game.

Forcing the machine on a real project, I realized one of them: Mutations (side effects) after doing a ton of code, two months of debugging, partial fixes, create several user stories of tech debt and endless meetings searching for a satisfactory solution, wow mutations are pain in the back!

After this experience my programmer life change forever. However, other concepts were a new game changer but, in this time, playing on my side: Pure Functions after creating clean functions, self-documented code, easy debugging, quick defect trace, separate concerns and decoupling code, this concep was provided by updated version of NgRx right now is using a functional programming approach entirely. Besides these learnings I was introduced to these concepts formally reading this book: ‘Professor Frisby’s Mostly Adequate Guide to Functional Programming’ and this including why we have side effects.

After these experiences I faced the Baader-Meinhof effect, I found the functional programming approaches everywhere even in the letter soup (spaghetti code??).

Understanding Functional Programming Concepts

After this storytelling let’s go to the bones, the first question is.

What is Functional Programming?

In computer science, functional programming is a programming paradigm where programs are constructed by applying and composing functions. Basically, is coding based on functions; we are developing and using a lot of functions in our current life as JavaScript Developer in classes, utils, methods, libraries, frameworks and so on without noticed. But What is the most important concept to said? We are hooked on the functional programming; we need to distinguish between side effects and pure function. Let’s see.

What are the side effects?

A side effect is a change of system state or observable interaction with the outside world that occurs during the calculation of a result (source Mostly-adequate-guide).

Side effects may include but are not limited to.

· Changing the file system

· Inserting a record into a database

· Making an http call

· Mutations

· Printing to the screen/logging

· Obtaining user input

· Querying the DOM

· Accessing system state

The most difficult to handle in my experience are the mutations in the state, how to fix a defect which solution code is producing more errors in another part of the application, then these new errors are fixed, and the previous solution is broken and not working as expected again. This is an endless Ouroboros, we can fix probably with a lot debugging, refactoring and time (Probably start again from scratch the module or the application would be a better idea). To prevent this chaotic and unpredictable scenario let’s see the next question.

What is a pure function?

A pure function is a function that, given the same input, will always return the same output and does not have any observable side effect.

The definition is pure itself; the parading tends to provide a predictable result, let’s code to demonstrate how to handle these concepts.

Pure and impure methods in JavaScript arrays (click on console to review the outputs)

In this case, slice is a pure function from JavaScript Arrays, keeping the initial data even if we have several executions. Meanwhile, splice is mutating the array in every usage causing data loss and unpredictability, is easy to see but what is happening when the involved array is used in two hundred lines of code.

To illustrate the impure and pure concepts let’s see the next samples. Analyzing each case, you will find these patterns in every code that you created and reviewed in your life.

Pure and impure samples (click on console to review the outputs)

Also, functional programming helps us in the future or present (depending on the unit test strategy), in the unit test creation, would be very easier to pass.

it('should return 33', () => {
const result = sum(30, 3);

expect(result).toEqual(33);
});

Let’s increase the concepts list, one of them is First-class Function.

First-class Function

A programming language is said to have First-class functions when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable. (source MDN Web Docs)

First-class sample (click on console to review the outputs)

This feature helps to use the functions as arguments and starts the simplification, this a quick introduction to the composable approach. In the meantime, this concept is responsible of High order functions, these functions right now are popular and are replacing old approaches. The definition is the following:

High Order Functions

Is a function that returns a function or takes other functions as arguments is called a higher-order functions. Talking about game changers, right now the most popular High Order functions implemented in JavaScript arrays are map, filter and reduce, these HOF are the everyday’s bread.

  • map: Transform and apply the array elements.
  • filter: Each array value is evaluated and return if pass.
  • reduce: Merge each value based on the initial value.
Map vs For loop (click on console to review the outputs)

The next samples illustrate how are handling high order functions (functional approach) versus the mutable approaches and demonstrated the first-class function behavior, the mutate function is requiring a lot of improvements and fix, is created to demonstrate the complexity of code that tends to side effects.

Samples of mutation vs pure functions (click on console to review the outputs)

Currying

The next concept is tricky but very useful when is learned and some stuff that is present only when you notice:

In mathematics and computer science, currying is the technique of translating a function that takes multiple arguments into a sequence of families of functions, each taking a single argument. (source wikipedia)

All these concepts are learned in mathematics, in our case is a function that receives multiple arguments into a sequence, each taking a single argument. It won’t fully execute unless each argument is executed, a regular JavaScript function is executed when arguments are passed by example parseInt the second argument is optional and execution is not required. Let’s check that these samples would be more illustrative.

The next function is limited to use two arguments.

const increment = (x, y) => x + y;
increment(1, 3) // 4

However currying functions allow you to increase and redefine in a scalable way.

const add = x => y => x + y;
const increment = add(1); // ƒ ()
const addTen = add(10); // ƒ ()

increment(1) // 2
addTen(2) // 12

The add function takes one argument and returns the increment function. This function is taking the first argument via the closure; this way is executing with the second argument. To properly use the currying approach, we need to add a helper function, this is provided by libraries by example Lodash, Underscore, Ramdajs and so on, not limited to create your own curry function. In the next sample the curried function is added just enjoy the implementation, we can see the flow of each execution very easily.

Currying demostration (click on console to review the outputs)

In this case the flow of the functions demonstrates the scalability, reusability, agnostic behavior, separation of responsibilities and many other features, is extremely useful to define an utils library and the maintenance would be easy. Each function is meaningful by its own way, getting self-documentation in automatic.

Composition

Finally, we are concluding this trip, we learned what a pure function is, understand first-class functions, how to recognize high order functions and how to currying a function. The milestone in this introduction to functional programing is start to chaining functions in a compose function, ironical We need to chain the learned concepts to understand what compose is.

Compose is a powerful technique that allows you to chain multiple functions into a single function running a function from the right to left, executes the chained functions as the input argument for the next one; promotes code reuse, modularity, readability, and the above benefits mention in this article.

Once you chain all the concepts, you can push the abstraction further. Imagine you start to play with Legos, just assemble each piece, each piece can match in smoothly way with others and each piece provides a unique experience and behavior, sounds very funny at this point. However, the compose function is not supported yet by ECMA 6 and importing from libraries is required, like currying. Don’t hesitate to use your own function.

Recalling the Currying sample, we can compose all the functions but in a clean and cool way. Let’s see (Understanding that We are importing the helper functions like compose, map, match, etc.).

Compose demostration (click on console to review the outputs)

Very readable and simple to maintain, compose also is providing attributes like associative behavior. This concept helps to group the functions in different ways and getting the same result. One stuff that is requiring attention is the order flow; in this case, is running from right to the left and is affecting in sequence, in the end you cannot match a simple square Lego with a Lego wheel you are requiring other adequate pieces to assembly.

Associative Demostration

In this sample, reverse and head functions are required in the initial side of the flow because is getting data from the array and remaining functions are transforming the output which are properly created to handling arrays. With this concept, we are wrapping all the concepts of this article.

Trends In JavaScript

Once you learned you will see everywhere, I gathered a collection of trends in JavaScript that are using functional programming approaches and combined with other patterns, we can find a lot of implementations out there (some of them are handling side effects a good topic to cover other day).

§ Array methods

 const array1 = [
 [0, 1],
 [2, 3],
 [4, 5],
 ];

 const result = array1.reduceRight((accumulator, currentValue) =>
 accumulator.concat(currentValue),
 );

 console.log(result);
 // Expected output: Array [4, 5, 2, 3, 0, 1]

§ Pipelines With Rxjs

 //emit outputs from one observable
 const example = merge(
 first.pipe(mapTo('FIRST!')),
 second.pipe(mapTo('SECOND!')),
 third.pipe(mapTo('THIRD')),
 fourth.pipe(mapTo('FOURTH'))
 );
 //output: "FOURTH", "THIRD", "SECOND!", "FOURTH", "FIRST!", "THIRD", "FOURTH"
 const subscribe = example.subscribe(val => console.log(val));

§ NgRx Library

 export const scoreboardReducer = createReducer(
 initialState,
 on(ScoreboardPageActions.homeScore, state => ({ ...state, home: state.home + 1 })),
 on(ScoreboardPageActions.awayScore, state => ({ ...state, away: state.away + 1 })),
 on(ScoreboardPageActions.resetScore, state => ({ home: 0, away: 0 })),
 on(ScoreboardPageActions.setScores, (state, { game }) => ({ home: game.home, away: game.away }))
 );

§ Lodash functions

 var users = [
 { 'user': 'barney', 'age': 36 },
 { 'user': 'fred', 'age': 40 },
 { 'user': 'pebbles', 'age': 1 }
 ];

 var youngest = _
 .chain(users)
 .sortBy('age')
 .map(function(o) {
 return o.user + ' is ' + o.age;
 })
 .head()
 .value();
 // => 'pebbles is 1'

§ RamdaJs

 const classyGreeting = (firstName, lastName) => "The name's " + lastName + ", " + firstName + " " + lastName
 const yellGreeting = R.compose(R.toUpper, classyGreeting);
 yellGreeting('James', 'Bond'); //=> "THE NAME'S BOND, JAMES BOND"

 R.compose(Math.abs, R.add(1), R.multiply(2))(-4) //=> 7

§ High Order Functions

 const arr = [5, 7, 1, 8, 4];
 const sum = arr.reduce(function(accumulator, currentValue) {
 return accumulator + currentValue;
 }, 10);
 // prints 35
 console.log(sum);

§ Functional Components

 import { useState } from 'react';

 export default function Counter() {
 const [count, setCount] = useState(0);

 function handleClick() {
 setCount(count + 1);
 }

 return (
 <button onClick={handleClick}>
 You pressed me {count} times
 </button>
 );
 }

§ React Compose

 import { compose } from 'react-compose';

 const Cat = props => {
 return <p>The cat is {props.age} years old</p>;
 };

 const injectAge = {
 age: 5,
 };

 const Composed = compose(injectAge)(Cat);

§ Angular Functions

 const canActivateTeam: CanActivateFn = (
 route: ActivatedRouteSnapshot,
 state: RouterStateSnapshot,
 ) => {
 return state.url === '/music/playlist' && route.outlet === 'primary';
 };

Conclusion

Thanks to this basic knowledge in functional programming, I improved a lot in how I code, helping to other developers as well, good code is received joyfully in every project, sometimes is required strictly.

Unfortunately, I faced the Dunning Kruger effect. The path to learn functional programming is starting, and the journey seems extraordinarily long and requires patience. For now improving functions would be a permanent achievement.

--

--