loading...

Functional Programming in JavaScript

hunter profile image Hunter Henrichsen Updated on ・5 min read

Functional programming is a really useful tool to have in your toolbox. For me, it's changed how I design solutions to a bunch of different problems.

What is Functional Programming?

Functional Programming is a programming paradigm where everything is a function, and the goal is to keep everything straightforward and mathematical. Variables are intended to be kept immutable, at least on a function-level, and are expected to do what they say without any side effects. A function printObject should not also add a field to the object it takes.

The most important part, though, can be inferred from the name of the paradigm: Functional Programming. In Functional Programming, functions are the most important. Normally, this takes the form of allowing them to be stored in variables, allowing them to be used in many of the same places where we could use any other type of variable.

There are many different purely functional languages out there, but for this post I'll be focusing JavaScript. I'm going to break it down into two pieces, with the first focused on built-in tools that allow for the use of Functional Programming, and then move onto how we can create our own functions and tools that use Functional Programming.

Functional Programming in JavaScript

JavaScript is very function-first. Assigning functions to variables is an important way to create them!

const printHello = function() {
    console.log("Hello!");
}

printHello();

Not only this, but there are multiple ways to create functions. One such way is using the function keyword like above, or using the function keyword before the name of the function.

function add(firstNumber, secondNumber) {
    return firstNumber + secondNumber;
}

console.log(add(2, 4)); // Prints "6"

A third way to create functions is using arrow notation.

const yellToConsole = (message) => {
    console.log(message.toUpperCase());
}

yellToConsole("Hello!"); // Prints "HELLO!"

There are some nuances between arrow and normal functions in JavaScript, which appear in the header of the MDN page for arrow functions.

Because doing things this way is not the only way to do things in JavaScript, it's said that JavaScript is not a purely functional programming language, but the primary aspect of Functional Programming--that functions can be assigned to variables--is an important piece of the language.

The Standard Library

Two of the main places that I see functional programming in the JavaScript standard library are Array Functions and Callbacks, especially when expanded to include many commonly-used libraries like Lodash.

Each array comes with some additional functions attached.

Map

The map function takes a function as it's argument, and transforms the array into something else.

const numberList = [1, 2, 3, 4];

const stringList = numberList.map((number) => number.toString()); // ["1", "2", "3", "4"]

Here I'm creating an arrow function as it's parameters, but you could just as easily accomplish the same by declaring a function any other way, then passing the reference to that function to the map function.

const numberList = [1, 2, 3, 4];

function square(number) {
    return number * number;
}

const squareList = numberList.map(square); // [1, 4, 9, 16]

Reduce

Reduce does a similar transformation, with the added effect of combining things together. It's used to convert an array into a single value.

const numberList = [1, 2, 3, 4];

const sum = numberList.reduce((currentSum, number) => currentSum + number, 0); // 10

Notice how we get the currentSum parameter to the function? That's called an accumulator, and represents everything that's been combined so far. We also have a 0 at the end of the function call, which tells JavaScript what that accumulator should start with. This can be an empty string, if we were building a string up based on the elements in the list.

Filter

Filter takes a function that's used to decide if something should remain in a list, then returns a new list of things that were supposed to remain in the list. The function that filter takes is called a predicate because it takes one value, and returns either true or false based on some logic.

const numberList = [1, 2, 3, 4];

const evens = numberList.filter((number) => number % 2 === 0); // [2, 4]

Filter can also be used to delete items from a list that we can't modify. How do you think we'd accomplish that?

Some

Some is a combination of filter and reduce. It's used to determine if any of the values in the list match the predicate.

const numberList = [1, 2, 3, 4];

const isOneInList = numberList.some((number) => number === 1); // true

Callbacks

Callbacks are used to perform processing when something finishes. These can be chained together to allow complex processing, but can become confusing when they get nested into many layers. This confusion is part of what the motivation for async/await was. Functions inside of then blocks are callbacks in Promises.

fetch('https://api.github.com/gists/e59384900942ee13af189bf92c7adfef')
    .then(response => response.json())
    .then(json => console.log(json.description)); // Prints "Predication in C# collections."

Each of these .then calls takes a function, and whatever it returns is passed to the next .then call.

Doing It Yourself -- Writing a simple Some function

So knowing how to use these is important, but let's write our own. An easy one to do would be to recreate the some function that I showed up above. First, I'm going to decide on a signature for this function. It needs to take a list, and a function. I'm going to write a specification for these using JSDoc.

/**
 * @callback anyMatch~predicate
 *
 * A predicate that applies to any item.
 *
 * @param {any} item The item to check this condition on.
 * @returns {boolean} Whether this condition passed or not.
 */

/**
 * Imitation of the array 'some' function.
 * 
 * @param {any[]} list The list to process.
 * @param {anyMatch~predicate} predicate The predicate to apply to each item.
 * @returns {boolean} Whether any item in this list matched this condition.
 */
const anyMatch = function(list, predicate) {

};

Next we can loop through, checking each item against the predicate:

const anyMatch = function(list, predicate) {
    for(const item of list) {
        if(predicate(item)) { // The item matched the predicate.
            return true;
        }
    }
    return false; // Nothing matched the predicate.
};

Notice how we can call predicate just as if it were any other function? That's all there is to it. Now let's run some tests:

console.log(anyMatch([1, 2, 3, 4], (item) => item === 1)); // Prints "true"
console.log(anyMatch([1, 2, 3, 4], (item) => item === 5)); // Prints "false"

And it's working! I hope this was at least a little enlightening. What common scenarios do you see approaches like this working for? Let me know in the comments.

Posted on Jun 2 by:

hunter profile

Hunter Henrichsen

@hunter

He/Him. CodingCoach mentor. Computer Science, Math, and English student. I like to write, whether that's code, fiction, or somewhere in between.

Discussion

markdown guide
 

Great post, thanks for sharing. 👏

I'd like to add that JavaScript used to be a Lisp dialect, so has its roots in FP.

Only "recently" do we see a lot of OOP concepts finding their way into JS (especially through ECMA standardization).

Now add the fact that JavaScript was actually a misnomer (it had nothing to do with Java or Java's foundation) and we might understand why many devs from an OOP background struggle with JS at the beginning.

Of course, the latter is mitigated through ECMA's standardization efforts that have added a lot of OOP ideas to JS.

 

JavaScript actually does have a bit of a connection to Java. See this article for more detail, but I'll try to summarize it.

Netscape thought that bringing Java to the web was a great idea and they were working with Sun Microsystems to make this happen. The purpose of JavaScript was to be a scripting language that was easy to use (for amateurs, as opposed to the more enterprise-y Java). JavaScript was originally supposed to be Scheme-like, but the partnership with Java forced it to become more Java-like.

It wasn't called JavaScript at first. It started as Mocha (to connect it thematically to Java), but then became LiveScript (to avoid any legal issues before Netscape sealed the deal with Sun). It was renamed to JavaScript after the deal closed, because it was supposed to work very closely with Java (see LiveConnect)

Of course, being a Scheme language with the requirement that it looks like Java was a surefire way to confuse anyone who first looked at the language.

 

Thank you very much for the insight. That is entirely news to me. It is interesting how much political/legal energy is put into what is otherwise technical decision making. 👍

 

I started with Java, and definitely experienced that. I heard a comparison that comparing Java to JavaScript was like comparing a car to a carpet, but I don't think that holds as much anymore because the JS ecosystem has really developed in the past few years.

I think adding the option to do OOP in JS is a good direction to take, as letting developers choose which paradigm to use for each situation is really powerful. That power is another reason I see the same approach being adapted in Java 8+ and other languages.

 

The car / carpet thing is awesome. That's exactly how it used to be.

And I second what you said: JS has come a long way and it's good that we can use it as a full-grown OO language now.

It's also nice to see that so many popular languages are opening up to all different sorts of paradigms. Gives us developers lots of options and flexibility.

 

There were three new features in ES6 relevant to FP: arrow syntax for lambdas, the Promise abstraction and tail call elimination. Browser vendors skipped the latter, because the teams in charge were biased for OOP and probably the respective companies as well. The Promise Aplus design process was also biased against FP, because the key players were again employed by the very same companies.

 

Instead of looping we could use find too right ? We really don't like to use for/while etc unless we have to

const anyMatch = (list,predicate) => {
    return list.find(item => predicate(item)) != null
}
 

We could, but for someone who is not familiar with functional programming it's a little strange. We could also do this:

const anyMatch = (list, predicate) => list.some(predicate);

The point is to illustrate how this would work without applying functional programming to everything in order to build some understanding using familiar constructs.

 

Hey Hunter!

Great post, thanks for sharing!

I think I spotted a slight mistake with your section around reduce .

The function signature of reduce is

const reducer = (accumulator, currentValue) => accumulator + currentValue;

i,e accumulator first, then currentValue whereas your description says "Notice how we get that second parameter to the function? That's called an accumulator, " and your code block has them flipped too:

const sum = numberList.reduce((currentSum, number) => currentSum + number, 0); // 10

You may not have noticed as in this case it would still calculate correctly regardless of the naming of the variables for a simple sum like this. Just thought you should know as it threw me off slightly reading it!

 

The currentSum is meant to be the accumulator, so I think the code block is correct it's just the language above it that's ambiguous. I had intended it to be something that meant closer to "notice how we have a new parameter" rather than implying the number was the accumulator. Either way, I'll go back and try and clear that language up. Thanks for pointing this out!