Functional Programming in JavaScript

The general desire for functional programming comes from two main perspectives:

  • flexibility
  • function’s natural mappings to many programming tasks

Imagine the following dataset where we are tasked with finding out the age of everyone in a large data table

NameBirthday
David2/24/2005
John2/18/2002
Sue9/9/2009
Amanda9/18/2001

Using declarative programming (what we are probably familiar with, you would likely use something like this)

const dataFrame = [
  ['David', '2/24/2005'],
  ['John', '2/18/2002'],
  ['Sue', '9/9/2009'],
  ['Amanda', '9/18/2001'],
];

const result = [];

for (const row of dataFrame) {
  const now = new Date().getFullYear();
  const birthday = new Date(row[1]);
  
  result.push(now - birthday.getFullYear());
}

console.log(result);

However, this code masks what we are doing. Everyone’s birthday calculation is independent. The for loop is doing the same operation for each row. When thinking about improving this code, we could first pull out the functionality of getting birthdays in years.

function getBirthdayInYear(input) {
  /*
   * Takes a standard American date in the format %m/%d/%Y (ex: 2/5/2001)
   * and calculates the difference between that and today's date
   */
  const now = new Date().getFullYear();
  const birthday = new Date(input);
  
  return now - birthday.getFullYear();
}

const resultSet = [];

for (const row of dataFrame) {
  resultSet.push(getBirthdayInYear(row[1]));
}

This code is better because what the code is doing getBirthdayInYear is clearly spelled out. If fact, because applying things to lists like this and compiling the results is so common, Python has this as a built-in feature.

const resultSet = dataFrame.map(row => getBirthdayInYear(row[1]));

Functions as arguments

Functions are a great way to customize the behavior of classes. For this, reason functions are often passed as arguments to other functions.

function myFunc() {
  console.log("My function!");
}

const x = myFunc; // setting x to a function!
x(); // Prints: My function!

A good example of why this can be incredibly useful is the TenorFlow API itself. Without getting too technical, models have a few key characteristics: their structure or what kind of layers make them up, their loss function and their training data.

In TensorFlow all of these are functions:

  • Sequence - A layer cake of types for defining how input flows from the beginning to the end
  • The loss function takes what the model predicted and the correct output and measure how bad the model is
  • Training data comes in the form of a pipeline which generates a new batch of samples every time it is called

Anonymous (Lambda) functions

get_birthday_in_year is a unique function which we might want to test and reuse in multiple parts of our code. However, what if our function was doing something trivial and the name or docstring of the function was not really important. This is where lambda functions come in.

Lambda functions are anonymous functions (they don’t have a name), but still take input an output.

Why are they called lambda functions? Well, mathematicians like using the $\lambda$ symbol so that could be why

Imagine I have a class representing a person:

class Person {
  constructor(name, age) {
    /*
     * Creates a new person
     */
    this.name = name;
    this.age = age;
  }
}

const people = [
  new Person("Joe", 23),
  new Person("Sue", 44),
  new Person("Bob", 91),
  new Person("Sam", 32)
];

A I have a list of these objects and would like to sort them. Those familiar with Java might be inclined to use a compare_to method which tells which comes first in a sequence. However, you only get one of these. What if you wanted to sort people by both name and age.

JavaScript has an Array.prototype.sort() method for custom sorting. To know where to place each item, JavaScript has two know, given two numbers, which one comes before the other.

This is where arrow (lambda) functions come in. Arrow functions are shorthand developers created for functions which do not have to have names. Arrow functions use the (\* inputs *\)=>{\* code *\} syntax.


function myFunction(){
  console.log("My function!");
}

// is the same as

const myOtherFunction = () => {
    console.log("My Other Function");
}

So, to sort by name, we define a function (a, b) => {} which compares and a and b either by name or age.

// Comparing by name
const sortedByName = people.sort((a, b) => a.name.localeCompare(b.name));

// Comparing by age
const sortedByAge = people.sort((a, b) => a.age - b.age);

Here, the power of arrow functions as they provide a way to pack a large amount of functionality into relatively little code.

Functional Programming (FP) versus Object-Oriented Programming (OOP)

Using functions in your code like this is known as Functional Programming (FP) which can serve in contrast to Object-Oriented Programming (OOP). While functional programming has a ton of advantages, try and not go too trigger happy. Objects still serve a purpose and can be a great way to group data in a neat and organized way. Though more limiting in some ways, objectification can often force you to think about how to structure your data more intelligently.

Summary

Functional Programming (FP) should be an incredibly useful tool in your arsenal in for this course in particular and particularly in terms of machine learning.