We're planting a tree for every job application! Click here to learn more

JavaScript Functional Style Made Simple

Daniel Boros

15 Sep 2021

•

10 min read

JavaScript Functional Style Made Simple
  • JavaScript

This tutorial will show you an easy way to learn and use a functional style of programming in Javascript. If you know how to program in Javascript, but never tried functional programming, this is the tutorial for you.

What is Functional Programming?

Functional programming is an old way of constructing code, and it has certain advantages and disadvantages. The main idea is to use pure functions only for any and all implementation of the business logic as well as for everything else.

A pure function is one that takes a certain input and from that produces a specific output and always the same output for the same input. In other words in a pure function cannot be affected by a side effect like the ones that are common object oriented programming.

In OO the output of a method is often altered by the state of the object the method was called on. Similarly, variables defined globally or declared in a closure above a function and used by a function would also produce an impure function. With the exception of the function parameters, only constants and other functions can be used in the body of a pure function.

Functional vs Imperative or OO programming

The benefit of functional programming is testability and reliability. Since everything is made from pure functions it is trivially easy to write automated tests for them since pure functions always conform to the de facto structure of tests. There is no need to set up the correct state in which the tests must be run because pure functions don't need the state only the parameters. Rather than quarantining the state as in OO via encapsulation functional programming simply bans side effects altogether.

The downside is that providing the data for a specific function can be much more challenging than in OO where you can access almost anything you might need using the this keyword. The upside though is that you no longer have ambiguous keywords like this which can mean anything and everything and are defined by the context rather than you. Another benefit of functional programming is the reusability of code that OO doesn't possess or is very hard to achieve in that paradigm. It should also be noted that some of the most critical infrastructures such as telecommunications are often programmed using a language designed for functional programming simply because the functional code is much less error-prone.

Functional Programming vs Functional Style Programming

In any sort of functional programming, we write functions and then we create new functions by composing them together. In imperative programming composition is usually in the form of fn1(fn2(fn3(val))), but in the functional paradigm we use a different syntax (see later), but in essence, it is the same. Since we are composing functions rather than calling them sequentially controlling the flow of the program becomes difficult. For example inserting a if else statement into a composition of functions is not trivial, since you cannot branch inside normally.

In functional programming, there are several structures and functions that are designed to help with branching and other difficulties such as side effects caused by IO. You might have heard some of these like Monads and Functors which are part of the Fantasy Land Specification that most functional programmers use. If you are programming in a functional style as opposed to raw functional programming, then you don't need to know about or understand these constructs that would be a requirement normally.

Since JavaScript is a multi-paradigm language, so you can still use if else or closure and other features of the language instead of composition, but things like this and OO, in general, should be avoided if possible. In essence not using Monads and similar constructs is what differentiates Functional from Functional Style Programming. There is no hard rule to avoid these constructs and if you find that you are not able to do a function composition where you normally could, chances are you are in need of a Monad or something similar. Therefore, if you do not want to break up your composition into smaller chunks it could be advisable to look them up or create you own flow control constructs.

Setting up the Functional Library

A function library is generally required for efficient functional programming, and there are a few we could choose from (like Ramda), but my favorite is lodash/fp.

"Why?" you might ask. There are a few reasons:

  • Lodash is a very popular library
  • The functions in fp are almost the same as in regular lodash
  • lodash/fp has everything you might need for functional style programming,
  • It has documentation that even normal humans can understand

In general lodash/fp is probably the easiest to use functional library and if you already know lodash it will be pretty easy to memorize the few functions essential for functional programming.

Assuming that you are using NodeJS v16 and Yarn you would normally install lodash like so:

yarn add lodash && yarn add -D @types/lodash

The types are installed only so that your IDE can have better autocomplete and is completely optional. You can use TypeScript for functional programming, but I recommend against it. TypeScript was developed to support OO programming and has difficulty figuring out the type signatures for compositions and there aren't many clean workarounds for when it fails to do so. Therefore, simple JS is the right choice at this moment.

When you are loading the library in NodeJS you should not use the ES loader unless you know what you are doing. If you insist on using ES modules remember that you can always go back to CommonJS modules.

const _ = require('lodash/fp')

you could also import/require individual functions from the library. It is up to you.

Functional Style Programming

The primary goal is to write simple functions and then composing them together to create more complex functions and then create even more complex functions by composing those as well. Composing functions is similar to method chaining in lodash except that essentially any function can be composed or made composable. It is a much more universal way of gluing instructions together. Consider the following example:

Let's say that we have a list of players in a traditional poker game like so:

const game = {
  players: [
    {
      playerId: 1,
      name: 'Tom',
      cards: [
        { color: 'diamonds', number: 2 },
        { color: 'clubs', number: 2 },
        { color: 'hearts', number: 2 },
        { color: 'spades', number: 2 },
        { color: 'hearts', number: 10 },
      ]
    },
    {
      playerId: 2,
      name: 'Bob',
      cards: [
        { color: 'spades', number: 3 },
        { color: 'spades', number: 4 },
        { color: 'spades', number: 5 },
        { color: 'spades', number: 6 },
        { color: 'spades', number: 7 },
      ]
    }
  ]
}

Suppose that we want to count the number of spades in the game. How would we do that? If we are starting from the upmost level, first we would have to get the players object, then map the cards in each object so that we will have an array of array of cards. Then we would flatten this array to a singe level and filter the spades and finally determine the size of the resulting array. In lodash/fp we would compose this functions so:

const countSpades = _.compose(
  _.size,
  _.filter(['color', 'spades']),
  _.flatten,
  _.map('cards'),
  _.get('players'),
)
console.log(countSpades(game)) // 6

Composition invokes functions from the bottom to the top similarly to how regular composition runs from right to the left. We could use the unix like pipe to reverse the order in which the functions are called, but I advise against that. This way the function call closest to the function name will be the last one being called meaning that you will be able to tell what the function returns just by looking at the top. In the above case the composed function will return a number.

Counting the number of spades is nice and all, but what if I want to count any color? What if I need a parameter to define the color we are interested in? The easiest way would be by using closure:

function countCardsWithColor (color) {
  return _.compose(
    _.size,
    _.filter(['color', color]),
    _.flatten,
    _.map('cards'),
    _.get('players'),
  )
}
console.log(countCardsWithColor('spades')(game)) // 6

We could even add the game as a parameter as a way to clarify what the input of this function would be, like so:

function countCardsWithColor (color, game) {
  return _.compose(
    _.size,
    _.filter(['color', color]),
    _.flatten,
    _.map('cards'),
    _.get('players'),
  )(game)
}
console.log(countCardsWithColor('spades', game)) // 6

But now you see we have a problem, because a function with more than one parameter (which is not unheard of) is not composable. Since in a composition one function passes on its returned value to the next function right above it, since only one value can be returned the function receiving it will only get that one as a parameter.

How is that possible, when we are clearly using get, map and filter which require more than one params? The answer is currying. In lodash/fp every function is curried meaning that if the function is called without every parameter, then a new function is returned (instead of calling the function) with the supplied parameters that can be called with the remaining params to call the function. This is called partial application. For example the function get(attribute, object) is called with the attribute name of 'players' producing a function that will be now composable because it will only need a single param: object. In lodash/fp every function is curried by default, but if we want to create our own composable functions we will need to call the curry function like so.

const countCardsWithColor = _.curry(function (color, game) {
  return _.compose(
    _.size,
    _.filter(['color', color]),
    _.flatten,
    _.map('cards'),
    _.get('players'),
  )(game)
})
console.log(countCardsWithColor('spades')(game)) // 6

Every named (not inline) function you define should be curried. One drawback of curried functions is that they cannot have any optional parameters. This is why many functions in lodash/fp are only called with the absolutely necessary parameters instead of adding all the optional params we are used to from the regular lodash library. Using a fixed number of params is a must in functional programming. Functions like these are commonly referred to as fixed arity functions.

Now that countCardsWithColor is curried we could create the previous countSpades by partially applying the first parameter spades to it:

const countSpades = countCardsWithColor('spades')

Not all functions need to be curried. For example inline functions probably don't need any currying. Personally I prefer to use the arrow notations for inline functions, because it is shorter. In the filter of lodash/fp in countCardsWithColor we could replace ['color', color] with (card) => card.color === color (since it is a lodash predicate) like so:

const countCardsWithColor = _.curry(function (color, game) {
  return _.compose([
    _.size,
    _.filter((card) => card.color === color),
    ...

If you have used lodash before you probably realized that the parameters for filter, map and get in lodash/fp are reversed (not counting the optional parameters that were removed). This is because in functional style programming functions are usually the first parameter rather than the last, because this makes composition easier. Function variables tend to be defined using const which means they are not likely to change and that makes them partially applicable. However, if you need to partially apply the second or the third parameter instead of the first, that can be done as well. For example if we had to use the old map function of lodash map(collection, iteratee) instead of map(iteratee, collection) of lodash/fp, we could achieve the same by placing a _ if we included lodash/fp as the underscore. For example this:

const _ = require('lodash/fp')
const map = _.curry(require('lodash').map)
const { players } = game
console.log(map(_, 'name')(players))
console.log(_.map('name')(players))

Would both log [ 'Tom', 'Bob' ] . This trick can be quite useful. Consider the possibility that you have an object like a config that cannot be changed (For the sake of simplicity we will use the players object). How would you write a function that would find a player object based on his or her name? Since players is a constant the parameter will be the name attribute rather than the players object which we will have to bind to the function somehow.

One simple solution would be this:

const { players } = game
const findUserByName = _.curry(function (name) {
  return _.find(['name', name], players)
})

This works, but we could do better than that. We could use the _.matchesProperty function to match the name like so

return _.find(_.matchesProperty('name', name), players)

Why is this better? You might ask. It isn't, but since matchesProperty is a function we could partially apply it, and we can insert a _ as the first parameter where that function will be called. Then we can compose them together:

const findUserByName = _.compose(
  _.find(_, players),
  _.matchesProperty('name')
)

Doesn't that look better? By doing it like this the players object is no longer reached using closure, because it is just a simple composition. There are several functions like matchesProperty in lodash so if you are creative enough you might be able to rewrite many less attractive functions into compositions.

Creating functions via composition is the foundational way functional programming is done, but if you don't need to reuse the function elsewhere you can resolve the function instead of giving it a name. If we only needed, let's say Bob we could get his player object by:

const playerBob = _.compose(
  _.find(_, players),
  _.matchesProperty('name')
)('Bob')

This is perfectly fine. You don't need to clutter your namespace with unnecessary names, and you can always convert this to a function if you need to run it multiple times. You can also use this as a value to be returned, if necessary.

Our composed functions may become very complex, and so we might loose track of what's going on. It is important to give our function compositions highly descriptive names. They should be as long as they need to be. Another helpful addition to anyone's library is a logging function, that if added to a composition will log out the current value and then return it as well. This way the rest of the functions can be called as well and logged as well.

const composeLog = _.curry(function (at, obj) {
  console.log(at)
  console.log(obj)
  return obj
})
const countSpades = _.compose(
  _.size,
  composeLog(4),
  _.filter(['color', 'spades']),
  composeLog(3),
  _.flatten,
  composeLog(2),
  _.map('cards'),
  composeLog(1),
  _.get('players'),
)

Rules for (my) Functional JS Style

  1. Learn Lodash & use Lodash/fp
  2. Use the (Lodash/fp) documentation, if you lack something it is probably there
  3. Only pure functions, but use closure when necessary
  4. Compose if you can const fnName = _.compose(...functions)
  5. Curry and return composition if you can't compose
    • const fnName = _.curry(function () {})
  6. It is okay to resolve a composition immediately to get a value
  7. If lodash/fp lacks a basic utility function make your own
  8. Function params should start with functions and params that are likely to be partially applied
  9. No optional parameters and no default values for any parameter
  10. Named functions should be declared with the function and inline functions using the arrow notation
  11. Collect similar functions/compositions in collections (arrays)
  12. Switch to imperative programming to control program flow or if you can't find a functional solution (refactor when possible)
  13. No OO unless when an external library requires it
  14. No Try... catch except when parsing JSON
  15. No TypeScript unless absolutely required
  16. Store your program's state in one or in a few objects
  17. Use Monads and other functional utilities if you have the knowledge and confidence on how to use them
  18. Simplicity & Correctness over Efficiency (this is JavaScript after all)
  19. Do not forget to write sample data sets and tests
Did you like this article?

Daniel Boros

I enjoy writing code and it has been a part of my life for so long that, I can hardly imagine living without it. My goal is always to write flawless, optimal, self documenting code that, if I or anyone else will need to change, can do so with ease. I like to improve by coding, with the help of others or without, but nevertheless improve myself whenever possible.

See other articles by Daniel

Related jobs

See all

Title

The company

  • Remote

Title

The company

  • Remote

Title

The company

  • Remote

Title

The company

  • Remote

Related articles

JavaScript Functional Style Made Simple

JavaScript Functional Style Made Simple

Daniel Boros

•

12 Sep 2021

JavaScript Functional Style Made Simple

JavaScript Functional Style Made Simple

Daniel Boros

•

12 Sep 2021

WorksHub

CareersCompaniesSitemapFunctional WorksBlockchain WorksJavaScript WorksAI WorksGolang WorksJava WorksPython WorksRemote Works
hello@works-hub.com

Ground Floor, Verse Building, 18 Brunswick Place, London, N1 6DZ

108 E 16th Street, New York, NY 10003

Subscribe to our newsletter

Join over 111,000 others and get access to exclusive content, job opportunities and more!

© 2024 WorksHub

Privacy PolicyDeveloped by WorksHub