This is the curriculum for the 2018 Foxtrot Web Developer Bootcamp.

Introduction from LEARN on Vimeo

Functional Programming

Functional programming is a paradigm that will change the way you think about code. Its a way to structure your code that will make you feel like you have super powers. You'll write code that is easier to reason about, easier to test, and much, much clearer when you and your peers look at the code again down the road.

But, what is functional programming?

Functional programming is writing functions that are stateless and have no side affects. This means that given specific inputs, a function will always return the exact same outputs, and never change any part of the larger system.

Here is a non-functional program that changes state of the system outside of the function:

let populations = {
  bear: 10000,
  gecko: 6000,
  cat: 45000000
}

function addPopulation(animal, increase){
  populations[animal] = populations[animal] + increase
}

This function changes the 'populations' object, which is a side effect, and can lead to bugs. The state of the application is uncertain. Depending on how many times this function is called, and with what values, you can never be certain what the outcome of the function will be without tracing the all the way back to the beginning to see how the state has been changed.

Now, let's look at at a purely functional function that does something similar:

function addPopulation(animal, increase){
  let newPopulation = populations[animal] + increase
  let myPopulations = Object.assign({}, populations, {[animal]: newPopulation})
  return myPopulations
}

This function does not change state, but rather creates a new object, and returns it. It leaves the populations object just as it was before it was called. This is a much safer function to write. You know exactly what the output will be, given known inputs.

Uncle Bob has a lot to say about functional programming

Have a look at what Uncle Bob says about functional programming. Uncle Bob's real name is Robert Martin, and he's very well known in the world of programming. His talks are highly entertaining, and always informative. He has been inspiring programmers to write better code for a long, long time.

This particular talk is going to get into a lot of new concepts and programming languages that you may not be familiar with. Don't get too bogged down in those details, and focus on his ideas about functional programming.

Pass By Reference from LEARN on Vimeo

Pass By Reference

In the following code, try and guess what the outcome will be, then run it for yourself, and see if you were correct.

let populations = {
  bear: 10000,
  gecko: 6000,
  cat: 45000000
}

function addPopulation(populations, animal, increase){
  populations[animal] = populations[animal] + increase
}

addPopulation(populations, 'bear', 100)
console.log(populations)

Did you guess that the populations object would be changed? It was, and this demonstrates that Javascript is a Pass-by-reference language. This means that when we pass arguments into a function we are actually passing a reference to that object, and not making a copy of it to pass in. So, when we change the object inside of the function, there is a side affect of the object changing in the scope of the calling function.

Pass-by-reference is an important concept to keep in mind when programming in Javascript, and many other languages. Its incredibly handy, but can lead to tricky bugs if its abused. When we're practicing functional programming, we need to be particularily careful with values that are passed by reference, because we want to avoid side effects in our functions at all cost. In fact, its one of the core tenants of functional programming that our functions should have absolutly no side effects outside of the function itself.

Declarative vs. Imperative from LEARN on Vimeo

Declarative vs. Imperative

When we talk about writing code in a 'imperative' style, or a 'declarative' style, we are talking about the difference of writing "what you want to do" (declarative) vs. "how you want to do it" (imperative).

Javascript makes heavy use of higher order functions, and this allows us to write more declarative code, which is generally considered better practice. Because we can name functions with variables, and pass those well named variables around, we can clearly state what we want our code to accomplish. Consider the following array:

let animals = [
  {name: 'bear', population: 10000},
  {name: 'gecko', population: 3400},
  {name: 'cat', population: 21000000}
]

Imperative

Writing in an imperative style, we can console.log out the animals and their population. This code is "how we get it done".

function listPopulations(animals){
  for(let i=0; i < animals.length; i++){
    console.log(`There are ${animals[i].population} ${animals[i].name}s`)
  }
}
listPopulations(animals)

Declarative

Declarative, on the other hand is much more descriptive, and easier to read. The code clearly states what we want to have done, and leaves the implimentation details to the functions it uses

function describeAnimalPopulation(data){
  console.log(`There are ${data.population} ${data.name}s`)
}
function listPopulations(animals){
  animals.forEach( describeAnimalPopulation )
}
listPopulations(animals)

Not only is this code easier to reason about, its also more reusable. We can reuse the describeAnimalPopulation anywhere else we may need to show that information.

Challenges

Refactor the following in a declarative style:

let fruits = [
  {name: "oranges", preference: "like"},
  {name: "pears", preference: "don't like"},
  {name: "apples", preference: "don't like"},
  {name: "bananas", preference: "like"}
]

function describe(array){
  for(var i=0; i<array.length; i++){
    console.log(`I ${array[i].preference} ${array[i].name}`)
  }
}

Array Mutator Methods

When working on Javascript arrays, there are several methods available to us that are mutators, meaning that they mutate the state of the array directly. Here is the list of mutator methods, and what they do:

  • Array.prototype.copyWithin()
    • Copies a sequence of array elements within the array.
  • Array.prototype.fill()
    • Fills all the elements of an array from a start index to an end index with a static value.
  • Array.prototype.pop()
    • Removes the last element from an array and returns that element.
  • Array.prototype.push()
    • Adds one or more elements to the end of an array and returns the new length of the array.
  • Array.prototype.reverse()
    • Reverses the order of the elements of an array in place the first becomes the last, and the last becomes the first.
  • Array.prototype.shift()
    • Removes the first element from an array and returns that element.
  • Array.prototype.sort()
    • Sorts the elements of an array in place and returns the array.
  • Array.prototype.splice()
    • Adds and/or removes elements from an array.
  • Array.prototype.unshift()
    • Adds one or more elements to the front of an array and returns the new length of the array.

We try to stay away from using these methods on arrays, but sometimes we are forced to because they are the only way to get done what we need to get done. sort() is a good example of this. When we need to sort values, we need to call sort. So how do we deal with this as functional superheros? Fortunately, there's an easy solution. We just create a copy of the array, and then we can call sort() on our new instance. Here's an example:

function sorted(array){
  let sortedArray = array.slice()
  sortedArray.sort()
  return sortedArray
}

let values = [4,2,19,2,33]
console.log(sorted(values))

slice() is a simple way to clone an array. When we call slice with no argument, it clones the entire array. If we give it an argument slice(2) for example, it would clone the array starting at the index of 2. Then, when we have a new copy of the array, we can sort it how we want, and return the new array, leaving the original array unchanged. That's functional happiness!

Challenge

Try to use some of the mutator methods for yourself in a functional style.

With the following array of values:
1) Add a new value to the beginning of the array.
2) Add a new value to the end.
3) Reverse the array.
4) Sort the array.

let values = [
  "apples",
  "pears",
  "bananas",
  "oranges"
]