SHARE
Facebook X Pinterest WhatsApp

Advanced Functional Programming: Monads in JavaScript

Written By
thumbnail
Diogo Souza
Diogo Souza
Aug 5, 2019

Have you noticed that, more and more frequently, the term Functional Programming is being used by the community?

In this article, we will show the more technical details for those who want to deepen their understanding of the subject.

So, keep reading this post to learn:

  1. Composition of functions
  2. Pointfree functions
  3. Using pipes with Pareto.js
  4. What are Monads

Function Composition

If you already understand the basics of Functional Programming, you have realized that one of the main goals is to build small (and pure) functions, so that we can compose them and develop other larger and more complex functions.

With that in mind, let’s build a functional solution to generate a simple slug of a string:

slug('New Blog Post') // new-blog-post

That is, we receive a string as a parameter and return a transformation of this string: 1) we apply a lowercase and 2) we substitute spaces by dashes.

The simple code below solves this problem:

const toLowerCase = s => s.toLowerCase()
const replaceSpaces = s => s.replace(/s+/g, '-')
const slug = title => replaceSpaces(toLowerCase(title))

slug('New Blog Post') // new-blog-post

The slug function, which is the main function, receives a string title as a parameter, then applies the toLowerCase function in title and returns this value so that the replaceSpaces function returns the new transformed string.

Thus:

slug('New Blog Post');
 1. toLowerCase('New Blog Post'); 
 2. replaceSpaces('new blog post');
 3. 'novo-post-no-blog'

In short, the slug function is a composition of functions.

Although we are already on a more functional path, we still have some problems with the slug function. Readability is one of them:

const slug = title => replaceSpaces(toLowerCase(title))

We read the function from left to right:

  1. replaceSpaces
  2. toLowerCase

But the slug function, as I showed earlier, applies these two functions in the opposite order in the way we read. So, how to solve this problem?

Pipes

Composition of functions is so common when using the functional paradigm, that a common standard for doing this composition is to use pipes.

JavaScript does not come with a pipe function in the language itself, so we’ll use a functional lib for this. We could use Ramda.js without any problem, but in this example, I will use Pareto.js, which is very similar to Ramda.js. The difference is that it is lightweight and more modern.

With the pipe function then, the problem would be solved this way:

import { pipe } from 'paretojs'

const toLowerCase = s => s.toLowerCase()
const replaceSpaces = s => s.replace(/s+/g, '-')

const slug = pipe(toLowerCase, replaceSpaces)

slug('New Blog Post') // new-blog-post

In this way, we reached the first goal, which was to improve the reading order of the function that is now more natural: from left to right.

In addition, we also get another more subtle advantage. Read the two versions of the slug function again:

// old version
const slug = title => replaceSpaces(toLowerCase(title))

// new version
const slug = pipe(toLowerCase, replaceSpaces)

The new version does not contain any reference to the title parameter that will be passed. This has a name: pointfree function.

With this, we do not get so coupled to a specific parameter name and we have a more flexible function to possible changes and easier to read.

Null

Our slug function works great, there’s only one problem — in the real world, we eventually pass null as a parameter. In that case, the function would break and we would not even know what happened.

We can solve this issue in a simple way:

const toLowerCase = s => {
 if (s !== null) {
  return s.toLowerCase()
 }
}

const replaceSpaces = s => {
 if (s !== null) {
  return s.replace(/s+/g, '-')
 }
}

But this solution is not very scalable: we need to put the null check on all our functions, which should be simple and focused on solving only one problem.

It would be much simpler to centralize this check in one place.

Monads

A Monad, in short, is simply a wrapper of any value. The Maybe function below does just that:

function Maybe(value) {
 return {
  value: value
 }
}

const maybeString = Maybe('New Blog Post')
maybeString.value // 'New Blog Post'

const maybeNull = Maybe(null)
maybeNull.value // null

With this, we already have a wrapper, but still some details are missing. First, let’s do a refactoring in the Maybe function, to take advantage of some benefits of ES6.

Let’s transform it then:

// ES5
function Maybe(value) {
 return {
  value: value
 }
}

To this:

// ES6
const Maybe = value => ({
 value
})

Since our main goal with Monads is to have a guarantee against null, let’s add an isNothing function that does this check:

const Maybe = value => ({
 value,
 isNothing() {
  return (this.value === null)
 }
})

const maybeString = Maybe('New Blog Post')
maybeString.isNothing() // false

const maybeNull = Maybe(null)
maybeString.isNothing() // true

To finalize our Monad, we need a new function that can:

  1. Apply another function to the value of the wrapper, if this value exists
  2. Do nothing if the value is null
const Maybe = value => ({
 value,
 isNothing() {
  return (this.value === null)
 },
 map(fn) {
  if (this.isNothing()) {
   return Maybe(null)
  }
  return Maybe(fn(this.value))
 }
})

const toLowerCase = s => s.toLowerCase()

const maybeString = Maybe('New Blog Post')
maybeString.map(toLowerCase) // Maybe {value: 'new blog post'}

const maybeNull = Maybe(null)
maybeNull.map(toLowerCase) // Maybe {value: null}

Now that we have the finished version of Maybe Monad, we just need to update the initial version of the slug function:

import { pipe } from 'paretojs'

const toLowerCase = maybeStr => maybeStr.map(s => s.toLowerCase())
const replaceSpaces = maybeStr => maybeStr.map(s => s.replace(/s+/g, '-'))

const slug = pipe(toLowerCase, replaceSpaces)

const maybeTitle = Maybe('New Blog Post')
slug(maybeTitle) // Maybe {value: 'New Blog Post'}

const maybeNull = Maybe(null)
slug(maybeNull) // Maybe {value: null}

Once this is done, we are able to compose functions in a functional way and protect ourselves from null.

Remember that we need to do all this because JavaScript is not an 100% functional language. Elm for example, already comes with everything we did in this post built into the language itself.

 	const toLowerCase = s => s.toLowerCase()
 	const replaceSpaces = s => s.replace(/s+/g, '-')
 	const slug = title => replaceSpaces(toLowerCase(title))

About the Author

Diogo Souza works as a Java Developer at PagSeguro and has worked for companies such as Indra Company, Atlantic Institute and Ebix LA. He is also an Android trainer, speaker at events on Java and mobile world.

Recommended for you...

The Revolutionary ES6 Rest and Spread Operators
Rob Gravelle
Aug 23, 2022
Ahead of Time (AOT) Compilation in Angular
Tariq Siddiqui
Aug 16, 2022
Converting a JavaScript Object to a String
Rob Gravelle
Aug 14, 2022
Understanding Primitive Type Coercion in JavaScript
Rob Gravelle
Jul 28, 2022
HTML Goodies Logo

The original home of HTML tutorials. HTMLGoodies is a website dedicated to publishing tutorials that cover every aspect of being a web developer. We cover programming and web development tutorials on languages and technologies such as HTML, JavaScript, and CSS. In addition, our articles cover web frameworks like Angular and React.JS, as well as popular Content Management Systems (CMS) that include WordPress, Drupal, and Joomla. Website development platforms like Shopify, Squarespace, and Wix are also featured. Topics related to solid web design and Internet Marketing also find a home on HTMLGoodies, as we discuss UX/UI Design, Search Engine Optimization (SEO), and web dev best practices.

Property of TechnologyAdvice. © 2025 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.