[flaviocopes] The Advanced JavaScript Course - Functional JavaScript

Introduction to Functional Programming

Functional Programming (FP) is a programming paradigm with some particular techniques.

In programming languages, you’ll find purely functional programming languages as well as programming languages that support functional programming techniques.

Haskell, Clojure and Scala are some of the most popular purely functional programming languages.

Popular programming languages that support functional programming techniques are JavaScript, Python, Ruby and many others.

Functional Programming is not a new concept, actually its roots go back o the 1930’s when lamda calculus was born, and has influenced many programming languages.

FP has been gaining a lot of momentum lately, so it’s the perfect time to learn about it.

In this course I’ll introduce the main concepts of Functional Programming, by using in the code examples JavaScript.

Higher Order Functions

Functions that accept functions as arguments or return functions are called Higher Order Functions .

Examples in the JavaScript standard library include Array.map() , Array.filter() and Array.reduce() , which we’ll see in a bit.

Declarative programming

You may have heard the term “declarative programming”.

Let’s put that term in context.

The opposite of declarative is imperative .

Declarative vs Imperative

An imperative approach is when you tell the machine (in general terms), the steps it needs to take to get a job done.

A declarative approach is when you tell the machine what you need to do, and you let it figure out the details.

You start thinking declarative when you have enough level of abstraction to stop reasoning about low level constructs, and think more at a higher UI level.

One might argue that C programming is more declarative than Assembly programming, and that’s true.

HTML is declarative, so if you’ve been using HTML since 1995, you’ve actually being building declarative UIs since 20+ years.

JavaScript can take both an imperative and a declarative programming approach.

For example, a declarative programming approach is to avoid using loops and instead use functional programming constructs like map , reduce and filter , because your programs are more abstract and less focused on telling the machine each step of processing.

Immutability

In functional programming data never changes. Data is immutable .

A variable can never be changed. To update its value, you create a new variable.

Instead of changing an array, to add a new item you create a new array by concatenating the old array, plus the new item.

An object is never updated, but copied before changing it.

const

This is why the ES2015 const is so widely used in modern JavaScript, which embraces functional programming concepts: to enforce immutability on variables.

Object.assign()

ES2015 also gave us Object.assign() , which is key to creating objects:

const redObj = { color: 'red' }
const yellowObj = Object.assign({}, redObj, {color: 'yellow'})

concat()

To append an item to an array in JavaScript we generally use the push() method on an array, but that method mutates the original array, so it’s not FP-ready.

We instead use the concat() method:

const a = [1, 2]
const b = [1, 2].concat(3)
// b = [1, 2, 3]

or we use the spread operator :

const c = [...a, 3]
// c = [1, 2, 3]

filter()

The same goes for removing an item from an array: instead of using pop() and splice() , which modify the original array, use array.filter() :

const d = a.filter((v, k) => k < 1)
// d = [1]

Purity

A pure function :

  • never changes any of the parameters that get passed to it by reference (in JS, objects and arrays): they should be considered immutable. It can of course change any parameter copied by value
  • the return value of a pure function is not influenced by anything else than its input parameters: passing the same parameters always result in the same output
  • during its execution, a pure function does not change anything outside of it

Data Transformations

Since immutability is such an important concept and a foundation of functional programming, you might ask how can data change.

Simple: data is changed by creating copies .

Functions, in particular, change the data by returning new copies of data.

Core functions that do this are map and reduce .

Array.map()

Calling Array.map() on an array will create a new array with the result of a function executed on every item of the original array:

const a = [1, 2, 3]
const b = a.map((v, k) => v * k)
// b = [0, 2, 6]

Array.reduce()

Calling Array.reduce() on an array allows us to transform that array on anything else, including a scalar, a function, a boolean, an object.

You pass a function that processes the result, and a starting point:

const a = [1, 2, 3]
const sum = a.reduce((partial, v) => partial + v, 0)
// sum = 6
const o = a.reduce((obj, k) => { obj[k] = k; return obj }, {})
// o = {1: 1, 2: 2, 3: 3}

Recursion

Recursion is a key topic in functional programming. when a function calls itself , it’s called a recursive function .

The classic example of recursion is the Fibonacci sequence (N = (N-1 + N-2)) calculation, here in its 2^N totally inefficient (but nice to read) solution:

var f = (n) => n <= 1 ? 1 : f(n-1) + f(n-2)

Composition

Composition is another key topic of Functional Programming, a good reason to put it into the “key topics” list.

Composition is how we generate a higher order function, by combining simpler functions .

Composing in plain JS

A very common way to compose functions in plain JavaScript is to chain them:

obj.doSomething()
   .doSomethingElse()

or, also very widely used, by passing a function execution into a function:

obj.doSomething(doThis())

Composing with the help of lodash

More generally, composing is the act of putting together a list of many functions to perform a more complicated operation.

lodash/fp comes with an implementation of compose : we execute a list of functions, starting with an argument, each function inherits the argument from the preceding function return value . Notice how we don’t need to store intermediate values anywhere.

import { compose } from 'lodash/fp'

const slugify = compose(
  encodeURIComponent,
  join('-'),
  map(toLowerCase),
  split(' ')
)

slufigy('Hello World') // hello-world

First class functions

In a functional programming language, functions are first class citizens.

They can be assigned to variables

const f = (m) => console.log(m)
f('Test')

Since a function is assignable to a variable, they can be added to objects:

const obj = {
  f(m) {
    console.log(m)
  }
}
obj.f('Test')

as well as to arrays:

const a = [
  m => console.log(m)
]
a[0]('Test')

They can be used as an argument to other functions

const f = (m) => () => console.log(m)
const f2 = (f3) => f3()
f2(f('Test'))

They can be returned by functions

const createF = () => {
  return (m) => console.log(m)
}
const f = createF()
f('Test')

filter()

filter() is a very important method of an array.

This example iterates the array a and builds a new array with elements of a that returned true when running the function f() on each a element

const b = a.filter(f)

A good example of using filter() is when you want to remove an item from the array:

const items = ['a', 'b', 'c', 'd', 'e', 'f']
const valueToRemove = 'c'
const filteredItems = items.filter(item => item !== valueToRemove)
// ["a", "b", "d", "e", "f"]

Here is how you could remove multiple items at the same time:

const items = ['a', 'b', 'c', 'd', 'e', 'f']
const valuesToRemove = ['c', 'd']
const filteredItems = items.filter(item => !valuesToRemove.includes(item))
// ["a", "b", "e", "f"]

map()

map() is key method of an array when it comes to thinking in functional programming terms.

This example iterates a and builds a new array with the result of executing f() on each a element:

const b = a.map(f)

Given an array, we can use map() to create a new array from the initial one, and then filtering the result using filter() . This short example creates a new array to get the first letter of each item in the list array, and filters the one that matches A :

const list = ['Apple', 'Orange', 'Egg']
list.map(item => item[0]).filter(item => item === 'A') //'A'

reduce()

reduce() is another important method of an array.

reduce() executes a callback function on all the items of the array and allows to progressively compute a result. If initialValue is specified, accumulator in the first iteration will equal to that value.

a.reduce((accumulator, currentValue, currentIndex, array) => {
  //...
}, initialValue)

Example:

;[1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => {
  return accumulator * currentValue
}, 1)

// iteration 1: 1 * 1 => return 1
// iteration 2: 1 * 2 => return 2
// iteration 3: 2 * 3 => return 6
// iteration 4: 6 * 4 => return 24

// return value is 24

Write loops using map, filter, reduce and find

How to perform common operations in JavaScript where you might use loops, using map(), filter(), reduce() and find().

Loops are generally used, in any programming language, to perform operations on arrays: given an array you can iterate over its elements and perform a calculation.

Let’s see how to pick common needs and perform them using a Functional Programming approach , as opposed to using loops.

NOTE: I don’t recommend one approach over the other. I just want to introduce different ways to perform the same thing and maybe introduce you to new functions which you might have never used until now.

map , filter , reduce , find

Those are 3 really powerful array functions:

  • map returns an array with the same length,
  • filter as the name implies, it returns an array with less items than the original array
  • reduce returns a single value (or object)
  • find returns the first items in an array that satisfies a condition

map , filter and reduce were introduced in ES5, so you can safely use them as implemented in every browser since years.

find was introduced in ES6/ES2015.

They offer a more declarative approach , rather than an imperative approach (describe what should happen, not write every tiny bit of processing that should happen)

Execute something on every element with map

A loop would look like this:

const performSomething = (item) => {
  //...
  return item
}
const items = ['a', 'b', 'c']
items.forEach((item) => {
  performSomething(item)
})

With a declarative approach, you tell JavaScript to perform something on every element using:

const items = ['a', 'b', 'c']
const newArray = items.map((item) => performSomething(item))

This generates a new array, without editing the original one (what we call immutability)

Since we use a single function in the map callback function, we can rewrite the sample as:

const items = ['a', 'b', 'c']
const newArray = items.map(performSomething)

Finding a single element in the array

Sometimes you need to look for a specific item in the array, and return it.

This is how you would do so with a loop:

const items = [
  { name: 'a', content: { /* ... */ }},
  { name: 'b', content: { /* ... */ }},
  { name: 'c', content: { /* ... */ }}
]
for (const item of items) {
  if (item.name === 'b') {
    return item
  }
}

Here is the non-loop version, using find() (ES6+):

const b = items.find((item) => item.name === 'b')

Here is the same functionality using filter() (ES5+):

const b = items.filter((item) => item.name === 'b').shift()

shift() returns the first item in the array without raising an error if the array is empty (returns undefined in that case).

Note: shift() mutates the array, but the array it mutates is the one returned by filter() , not the original array. If this sounds unacceptable, you can check if the array is not empty and get the first item using b[0] .

For learning purposes (does not make much sense in practice), here is the same functionality using reduce() :

const items = [
  { name: 'a', content: { /* ... */ }},
  { name: 'b', content: { /* ... */ }},
  { name: 'c', content: { /* ... */ }}
]

const b = items.reduce((result, item) => {
  if (item.name === 'b') { result = item }
  return result
}, null)

filter() and reduce() will iterate over all the array items, while find() will be faster.

Iterate over an array to count a property of each item

Use reduce() to get a single value out of an array. For example, sum the items content.value property:

const items = [
  { name: 'a', content: { value: 1 }},
  { name: 'b', content: { value: 2 }},
  { name: 'c', content: { value: 3 }}
]

using a loop:

let count = 0
for (const item of items) {
  count += item.content.value
}

can written as

const count = items.reduce((result, { content: { value } }) => result + valu

Quiz

Welcome to the quiz! Try to answer those questions, which cover the topics of this module.

You can also write the question/answer into the Discord chat, to make sure it’s correct - other students or Flavio will check it for you!

  • what is a higher order function?
  • what do we mean by declarative programming?
  • what are the benefits of immutability?
  • what does it mean that functions in JavaScript are first class?
  • can you explain the concept of composition?
  • describe what filter() is useful for
  • describe what map() is useful for
  • describe what reduce() is useful for
  • write the code needed to run the run() function on an array of dogs names using map() . That function should print <dogname> is running! to the console
  • use reduce() to make a string with the names of the dogs contained in the array ['togo', 'lessie', 'hachiko', 'balto'] , separating with a comma until the last one is found. I want this string: "togo, lessie, hackiko, balto" .