[005.5] Functional Programming in JavaScript

Learn Functional Programming with Mori.js

Subscribe now

Functional Programming in JavaScript [12.26.2017]

Core Functional Programming with Javascript

Hello and welcome to this tutorial on functional programming with Javascript and Mori.js. Before we go too far though, it is worth stopping and making sure we are all on the same page. To do this, we need to ask, what exactly is functional programming?

What is functional programming?

In short, functional programming can be summed up as programming without side effects. You compose functions that don't mess with outside values. You then piece the functions together to create the functionality that you wish. One interesting facet of good functional programming is that your code can be self-documenting. Take this rudimentary example.

let x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const evenNumbers = x.filter(num => num % 2)
const newNumbers = evenNumbers.map(num => num * 2)
console.log(newNumbers)

That code may not look bad, however, it's not immediately obvious what we're doing here. Without comments, we would have to spend a short amount of time deducing down what is going on. Perhaps that doesn't seem bad right now while we're fresh, but when you're tired and you have had a long day, your brain isn't going to want to do that. Let's rewrite this in a functional manner to see the difference.

const extractEvenNumbers = list => list.filter(num => num % 2)
const doubleAllNumbers = list => list.map(num => num * 2)

let x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const evenNumbers = extractEvenNumbers(x)
const newNumbers = doubleAllNumbers(evenNumbers)
console.log(newNumbers)

There, notice that while this may be technically more code, we now have a situation where our logic is easier to reason about. By declaring these functions ahead of time, we can suddenly see exactly what is going on in the code here without need for extra comments. You will also see that these functions are completely independent in operation. They do not rely on outside values except for whatever is explicitly passed to them in the parameters.

Dealing with State

This moves us to an important point about functional programming, and that is the shift to removing state from our code as much as possible. There are no shared variables and no usage of outside variables. Take this for an example.

let x = [1, 2, 3, 4, 5]

function extractEvenNumbers () {
  return x.filter(num => num % 2)
}

This would not be an example of functional programming. Yes, you may have this function, but it depends on the outside variable x. Not good. Let's rewrite this in a functional manner.

let x = [1, 2, 3, 4, 5]

function extractEvenNumbers (list) {
  return list.filter(num => num % 2)
}

Now that is better. Our function no longer depends specifically on x. Instead, the function can act independently and simply take whatever list we pass to it.

Declarative vs. Imperative Code

Moving on, another facet of functional programming is that we have to change the way we think about writing code. Specifically, we have to move away from thinking imperatively to thinking functionally. How do we do this? Well let's write down a simplified cooking recipe for baking a cake.

Ingredients

1. eggs
2. flour
3. baking powder
4. cocoa powder
5. sugar
6. pecans
7. cream cheese

Steps.
1. Set oven to X temperature.
2. Mix flour, sugar, eggs, cocoa powder, baking powder, and milk in a bowl.
3. Prepare cake pan by spraying inside with non-stick spray.
4. Pour cake mixture into cake pan.
5. Place cake pan inside of the oven and bake for X number of minutes.
6. Chop up pecans
7. Mix chopped pecans, cream cheese, and sugar to make icing.
8. When cake is done baking, remove from oven and let cool.
9. When cake is cooled, spread the icing from earlier over the cake.
10. Eat and enjoy.

Straight forward enough, this is an example of our normal imperative line of thinking. So how would this recipe look differently if it were rewritten in a functional manner?

Ingredients

1. eggs
2. flour
3. baking powder
4. cocoa powder
5. sugar
6. pecans
7. cream cheese

Actions
Prepare your Cookware
1. Set oven to X temperature
2. Prepare cake pan by spraying inside with non-stick spray.

Prepare the Cake Mix
1. Mix flour, sugar, eggs, cocoa powder, baking powder, and milk in a bowl.

Bake the Cake
1. Pour cake mixture into cake pan.
2. Place cake pan inside of the oven and bake for X number of minutes.

Prepare Cake Icing
1. Chop up pecans
2. Mix chopped pecans, cream cheese, and sugar to make icing.

Finish and Enjoy
1. When cake is done baking, remove from oven and let cool.
2. Spread the icing from earlier over the cake.
3. Eat and enjoy.

Steps.
1. Prepare your Cookware
2. Prepare the Cake Mix
3. Bake the Cake
4. Prepare Cake Icing
5. Finish and Enjoy.

As you can see, by including our Actions (functions), we were able to simplify the logic of our recipe. When reading the recipe imperatively, it's hard to know which step is influencing which part of the cooking process. Am I working on the icing? The cake batter? The cookware? By writing in a functional manner, it makes easy to understand the recipe logic as well as go back and reference which part of the recipe they specifically need to focus on. This works in the same manner for code as you will see later.

Handling Lists

Moving on, a huge part of functional programming involves handling lists as well as other data structures. In normal imperative programming you may be familiar with code such as this...

let x = [1, 2, 3, 4, 5]

for (let num of x) {
  num * 2
}

console.log(x)

Nothing unusual, we're just doubling each number in x. And of course, this code works, but it's not flexible. We're both creating side effects and this code can't be reused. It's key to understand that in pure functional programming, we want to avoid using for loops. Even inside of our functions.

Instead, Javascript provides us three methods to work with that are core to functional programming. They are map, reduce, and filter. Let's check out examples of each below.

const x = [1, 2, 3, 4, 5]

const doubleAllNumbers = list => list.map(num => num * 2)
const extractEvenNumbers = list => list.filter(num => num % 2)
const sumAllNumbers = list => list.reduce((acc, num) => acc += num, 0)

const y = doubleAllNumbers(x)
const z = extractEvenNumbers(x)
const a = sumAllNumbers(x)

So what do each of these do? If you look here, map simply applies a function to all the items within a list. Next, we have filter, which will return a new list with all the items that meet a particular condition you define using a function. In this case, we return a new array filled with only the even numbers from our original list. Lastly, we have reduce which will apply a function against an accumulator and each value within a list. In this case, we return a sum of all the numbers within a list.

Put functions into a pipeline.

Now, let's turn our attention to one more aspect of functional programming and that is the principle of using our functions to compose pipelines with which we process the input values to generate our new output. If you look through previous code, you will see we have done this to a small extent but with Mori, we will be able to go one step further in creating a our pipeline with a literal pipeline method shown here.

const _ = require('mori')

const log = (...args) => console.log(...args.map(_.toJs))

const x = _.vector(1, 2, 3, 4, 5, 6)
const extractEvenNumbers = list => _.filter(_.isEven, list)
const doubleAllNumbers = list => _.map(num => num * 2, list)
const sumAllNumbers = list => _.reduce(_.sum, 0, list)

const z = _.pipeline(x, 
extractEvenNumbers, 
doubleAllNumbers, 
sumAllNumbers)

log(z)

Here you'll see that we have a literal pipeline method that first takes a list as a parameter then pipes that list through all of the functions listed in the parameters. As you can see, this is can make your code drastically easier to comprehend, debug, and run in general.

Conclusion

So we've gone through a lot of theory for our first day but don't worry, we will be building a small project putting all of this to practice soon enough. Tomorrow, we will go over an often overlooked part of functional programming that generally gets overlooked and that is working with Immutable Data Structures.