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.
Top comments (7)
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.
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
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 thenumber
was the accumulator. Either way, I'll go back and try and clear that language up. Thanks for pointing this out!Instead of looping we could use find too right ? We really don't like to use for/while etc unless we have to
We could, but for someone who is not familiar with functional programming it's a little strange. We could also do this:
The point is to illustrate how this would work without applying functional programming to everything in order to build some understanding using familiar constructs.
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.
I highly recommend mostly-adequate.gitbooks.io/mostly... for a deep dive into FP with JS.