Helder
Published on

Understanding concepts of functional programming with JavaScript

Let's understand the fundamental concepts of functional programming using JavaScript language.

The abbreviation FP will be used within this article to reference functional programming.

Object Example

In this article we'll use the following object in our practical examples like the following:

const animals = [
  {
    name: 'Max',
    species: 'dog',
    likes: ['bones', 'carrots'],
  },
  {
    name: 'Teodore',
    species: 'cat',
    likes: ['mice', 'carrots'],
  },
]

What is functional programming?

FP is the basis in Lambda Calculus - a formal system developed in the 1930s. Lambda Calculus is a mathematical abstraction that you could read more on Lambda calculus definition - Wikipedia.

The FP programming paradigm is focused on writing more functions and make function compositions.

Composition

Photo by Ricardo Gomez Angel

Function composition is a way to combine multiple functions to build one result.

In mathematical we write like f(g(x)) when receiving the results from g(x) will be passed to the upper scope called f.

See the following example:

const isDog = (animals) => animals.filter((animal) => animal.species === 'dog')
const isCat = (animals) => animals.filter((animal) => animal.species === 'cat')
const likeCarrots = (animals) =>
  animals.filter((animal) => animal.likes.find((like) => like.includes('carrots')))

console.log(likeCarrots(isDog(animals)))
// => [{ name: "Max", species: "dog", likes: ["bones", "carrots" ]}]

console.log(likeCarrots(isCat(animals)))
// => [{ name: "Teodore", species: "cat", likes: ["mice", "carrots" ]}]

Automate the Composition process

Let's create an automation to previous example:

const compose =
  (...fns) =>
  (x) =>
    fns.reduceRight((v, fn) => fn(v), x)

console.log(compose(isDog, likeCarrots)(animals))
// => [{ name: "Max", species: "dog", likes: ["bones", "carrots" ]}]

console.log(compose(isCat, likeCarrots)(animals))
// => [{ name: "Teodore", species: "cat", likes: ["mice", "carrots" ]}]

The method called compose will execute all functions from right to left using reduceRight.

Note: we've got the same results with a much more readable and cleaner code.

Normally, when want to execute sequentially, so we use the pipe method like the following example:

const pipe =
  (...fns) =>
  (x) =>
    fns.reduce((v, fn) => fn(v), x)

console.log(pipe(isDog, likeCarrots)(animals))
// => [{ name: "Max", species: "dog", likes: ["bones", "carrots" ]}]

console.log(pipe(isCat, likeCarrots)(animals))
// => [{ name: "Teodore", species: "cat", likes: ["mice", "carrots" ]}]

Note: we've got the same results from the composed method because we just filtered the results, however, in cases you want to print different value, with is a nice approach.

I recommend the article A quick introduction to pipe() and compose() in JavaScript where you may understand the concepts of pipe and compose better.

Immutability

In immutability, we consider elements that won't mutate itself when we add a new property or change.

See an example when I've added new animal and create a new instance from animals:

const tildorCat = {
  name: 'Tildor',
  species: 'cat',
  likes: ['mice', 'carrots'],
}
const mutatedAnimals = [...animals, tildorCat]

console.log(animals.length) // => 2
console.log(mutatedAnimals.length) // => 3

We've used the base animals to create a new instance with the new animal instance.

Keep in mind when talking about immutability the initial values won't change, instead, it creates a modified instance.

Pure Functions

Pure Functions

Pure functions are used to avoid side-effects so guarantee when you pass an input it will always return the same output.

You can read more about What is a Pure Function? - DEV Community 👩‍💻👨‍💻.

Wrapping Up

That's all folks! I tried to show some fundamentals concepts and my personal opinion about some use cases of FP, I hope it helps you.

Feel free to comment below if you have any questions, I'll be happy to help you.

Enjoy programming!

References