Functional Programming

What is functional programming?

Wikipedia (Functional Programming):

In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming paradigm, which means programming is done with expressions or declarations instead of statements.

What is functional programming?

  • Programming Paradigm
    • "a style of programming"
    • "a rule set for our code"
  • Express programs as a composition of functions
  • Avoid mutating data
  • Declarative programming

The goal of functional programming is to simplify

our code.

How do we get there?

  • Break logic down into smaller functions
     
  • Write Declarative and Composable Code
     
  • Mutation in Isolation and Moderation
    
  const greeting = "Hi, I'm "
  const name = "Clayton"

  console.log(greeting + name)
    
  => "Hi, I'm Clayton"

  function greet(name) {
      return "Hi, I'm " + name
  }

  console.log(greet("Clayton"))

  => "Hi, I'm Clayton"
 
 function checkCarStatus(driver, car) {
     let assembled;
    
     if(car.isTotaled) {
         if(driver.isMechanic) {
             assembled = true;
         }
         else {
             assembled = false;
         }
     }
     else {
         assembled = true;
     }
    
     return assembled && car.hasGas;
 }
 
 function isAssembled(driver, car) {
     return !car.isTotalled || driver.isMechanic
 }

 function checkCarStatus(driver, car) {
     return isAssembled(driver, car) && car.hasGas
 }

Immutability

Most bugs are caused by mutation issues.

 

What if we could eliminate unnecessary mutation and isolate the mutation that is needed from the logic of our program?

Pure Functions

A pure function is a function that satisfies two properties:

  1. The function's output is deterministic based on the function's input.
     
  2. No side-effects take place inside the function.

Pure Functions

  1. The function's output is deterministic based on the function's input.

    - The same input to the function ALWAYS  
      provides the same output
     
  2. No side-effects take place inside the function.

    - No state external to the function is changed inside the
      function.
      ex. global variables, writing to streams, etc.
  
  // Impure Function Example

  let count = 0;

  function incrementCountBy(x){
      
      count += x
    
      
      console.log('Incremeneted by: ', x)
    
      
      return Math.random()
  }
  
  // Impure Function Example

  let count = 0;

  function incrementCountBy(x){
      // side effect: mutating state outside the function
      count += x
    
      // side effect: writing to IO stream
      console.log('Incremeneted by: ', x)
    
      // return value is non-deterministic
      return Math.random()
  }
  
  // Pure Function Example

  function double(x) {
      return x * 2 
  }

Higher-Order Functions

(HOFs)

 

  1. Accepts one or more functions as arguments
     
  2. Returns a function
     

A higher-order function is a function does either of the following:


 // inovke is a higher-order function
 function invoke(f) {
	 return f()
 }

 function dummyFunction() {
	 return "I'm a dummyFunction"
 }

 const value = invoke(dummyFunction)

 value
 => "I'm a dummyFunction"

 function greetWith(greeting) {
	 return function(name) {
		 return greeting + name
	 }
 }

 const morningGreeting = greetWith("Good morning, ")

 const students = ["Samantha", "Kunal", "George"]

 let morningGreetings = [];

 for(let i = 0; i < students.length; i++) {
	 morningGreetings.push(morningGreeting(students[i]))
 }

 morningGreetings
 => [ "Good morning, Samanatha", 
      "Good morning, Kunal", 
      "Good morning, George" ]

Avoid List Iteration with HOFs

Looping through lists is some of the most  common code you'll write day-to-day.

 

Let's take a look at three higher-order functions that will let us avoid writing the same list iterations over and over again.

Map

The map function creates a new array with the results of calling a provided function on every element in the calling array.

 
 const words = ['dog', 'plant', 'bike']

 function makePlural(word) {
	 return word + 's'
 }

 const plurals = words.map(makePlural)

 words
 => ['dog', 'plant', 'bike']

 plurals
 => ['dogs', 'plants', 'bikes']

Usage:

Map

Exercise: Write a function whose goal is to take a list of numbers and increment each number in the list by 1.

Map


 function incrementEntries(list) {
	 let res = [];
	 for(let i = 0; i < list.length; i++) {
		 res[i] = list[i] + 1
	 }
	 return res
 }

 const nums = [1, 2, 3]
 
 const numsPlusOne = incrementEntries(nums)

 numsPlusOne
 => [2, 3, 4]

Exercise: Write a function whose goal is to take a list of numbers and increment each number in the list by 1.

Map


 function incrementEntries(list) {
	 return list.map(function(x) {
            return x + 1
        })
 }

 const nums = [1, 2, 3]
 
 const numsPlusOne = incrementEntries(nums)

 numsPlusOne
 => [2, 3, 4]

Reduce


 const nums = [1, 2, 3]

 const sum = nums.reduce(function(accumulator, currentValue) {
	 return accumulator + currentValue
 }, 0)

The reduce function executes a reducer function (that you provide) on each member of the array resulting in a single output value.

Usage:

Reduce

Exercise: Find the max element in a list of numbers

Reduce

Exercise: Find the max element in a list of numbers


 const counts = [23, 15, 6, 79, 12]

 let maxCount = -Infinity

 for(let i = 0; i < counts.length; i++) {
     if(counts[i] > maxCount) {
         maxCount = counts[i]
     }
 }

 maxCount
 => 79

Reduce


 function max(x, y) {
     if(x > y) {
         return x
     }
     return y
 }

 const counts = [23, 15, 6, 79, 12]

 const maxCount = counts.reduce(max, -Infinity)

 maxCount
 => 79

Filter

The filter function creates a new array with all elements that pass the test implemented by the provided function.


 function isEven(num) {
     return num % 2 == 0
 }

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

 const evens = nums.filter(isEven)

 evens
 => [2, 4]

Usage:

Writing Declarative Code

Imperative - describes how it is done.

Declarative - describes what is done.

 

 
 const counts = [1,2,3,4,5,6,7,8,9,10]

 let double_counts = []

 for(let i = 0; i < counts.length; i++) {
   double_counts[i] = counts[i] * 2
 }

vs.

 const counts = [1,2,3,4,5,6,7,8,9,10]

 function double(x) {
     return x * 2
 }

 const double_counts = counts.map(double)

Writing Declarative Code

  • Easier to reason about
    • You don't need to "be" the computer to understand what the code is doing.
       
  • Less Repetitive Code
    • Easier debugging and testing

What do we get from writing declarative code?

Functional Refactoring Examples


 function greetWith(greeting) {
	 return function(name) {
		 return greeting + name
	 }
 }

 const morningGreeting = greetWith("Good morning, ")

 const students = ["Samantha", "Kunal", "George"]

 let morningGreetings = [];

 for(let i = 0; i < students.length; i++) {
	 morningGreetings.push(morningGreeting(students[i]))
 }

 morningGreetings
 => [ "Good morning, Samanatha", 
      "Good morning, Kunal", 
      "Good morning, George" ]

Functional Refactoring Examples


 function greetWith(greeting) {
	 return function(name) {
		 return greeting + name
	 }
 }

 const morningGreeting = greetWith("Good morning, ")

 const students = ["Samantha", "Kunal", "George"]

 const morningGreetings = students.map(morningGreeting)

 morningGreetings
 => [ "Good morning, Samanatha", 
      "Good morning, Kunal", 
      "Good morning, George" ]

Functional Refactoring Examples

Exercise: Create a new list containing incremented versions of the even numbers in the original list


 const old_nums = [1, 2, 3, 4, 5];
 let new_nums = [];

 for (let i = 0; i < old_nums.length; i++) {
   if (i % 2 == 0) {
     new_nums.push(old_nums[i] + 1);
   }
 }

 new_nums
 => [3, 5]

Imperative Solution:

Functional Refactoring Examples

Exercise: Create a new list containing incremented versions of the even numbers in the original list


 function increment(x) { return x + 1 }
 function isEven(x) { return x % 2 == 0 }

 const old_nums = [1, 2, 3, 4, 5]
 const new_nums = old_nums.filter(isEven).map(increment);

 new_nums
 => [3, 5]

Declarative Solution:

Summary

Functional programming:

  • Paradigm of programming whose goal is to simplify our code.
     
  • How we do it
    • Express our programs in terms of small  composable functions.
    • Write declarative code
    • Avoiding data mutation when possible

 

Our goal is not to write functional code - our goal is simplicity, and functional programming is the tool we're using to achieve it.

 

Links to the slides, script, and other resources can be found in the Github Repo:

 

https://bit.ly/2SshXKX

 

 

Feedback form:

 

https://bit.ly/2GfKz4i

Made with Slides.com