Slide 1

Slide 1 text

Functional Programming Basics in ES6 Jeremy Fairbank jeremyfairbank.com @elpapapollo

Slide 2

Slide 2 text

Hi, I’m Jeremy jfairbank @elpapapollo pushagency.io blog.jeremyfairbank.com simplybuilt.com

Slide 3

Slide 3 text

What is a function?

Slide 4

Slide 4 text

What is a function?

Slide 5

Slide 5 text

What is a function? Relation that pairs each element in the domain with exactly one element in the range. Domain Range

Slide 6

Slide 6 text

What is a function?

Slide 7

Slide 7 text

What is a function?

Slide 8

Slide 8 text

What is a function?

Slide 9

Slide 9 text

What is a function? Domain

Slide 10

Slide 10 text

What is a function? Domain Range

Slide 11

Slide 11 text

Unique mapping from input to output! Domain Range

Slide 12

Slide 12 text

Ugh, math?

Slide 13

Slide 13 text

Alonzo Church

Slide 14

Slide 14 text

Alonzo Church • Church-Turing Thesis • Lambda Calculus • Represent computation in terms of “function abstraction and application using variable binding and substitution.”

Slide 15

Slide 15 text

λ-Calculus Represent computation in terms of “function abstraction and application using variable binding and substitution.”

Slide 16

Slide 16 text

Represent computation in terms of “function abstraction and application using variable binding and substitution.” λ-Calculus

Slide 17

Slide 17 text

Represent computation in terms of “function abstraction and application using variable binding and substitution.” λ-Calculus Bind “x” to function => parameters

Slide 18

Slide 18 text

Represent computation in terms of “function abstraction and application using variable binding and substitution.” λ-Calculus Substitution => apply arguments

Slide 19

Slide 19 text

λ-Calculus Functions are anonymous.

Slide 20

Slide 20 text

Functions are anonymous. λ-Calculus

Slide 21

Slide 21 text

Functions are anonymous. λ-Calculus X

Slide 22

Slide 22 text

Functions are anonymous. λ-Calculus Functions are expressions and units of computation.

Slide 23

Slide 23 text

λ-Calculus Functions are single input.

Slide 24

Slide 24 text

Functions are single input. λ-Calculus

Slide 25

Slide 25 text

Functions are single input. λ-Calculus

Slide 26

Slide 26 text

Functions are single input. λ-Calculus Break down into smaller pieces — currying!

Slide 27

Slide 27 text

That’s nice. What about functional programming?

Slide 28

Slide 28 text

λ-Calculus shows the power of functional computation. Functional programming brings it to life in computer science!

Slide 29

Slide 29 text

What is Functional Programming?

Slide 30

Slide 30 text

– Wikipedia “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.” What is Functional Programming?

Slide 31

Slide 31 text

TL;DR Programming without assignment statements.

Slide 32

Slide 32 text

Key Concepts • Declarative (vs. Imperative) • First Class Functions • Referential Transparency and Purity • Recursion • Immutability • Partial Application and Currying • Composition

Slide 33

Slide 33 text

Thar be ES6 ahead!

Slide 34

Slide 34 text

const greeting = 'hello'; let n = 42; n = 5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz']

Slide 35

Slide 35 text

var greeting = 'hello'; const greeting = 'hello'; let n = 42; n = 5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz']

Slide 36

Slide 36 text

var n = 42; n = 5; const greeting = 'hello'; let n = 42; n = 5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz']

Slide 37

Slide 37 text

const greeting = 'hello'; let n = 42; n = 5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz'] var add = function(x, y) { return x + y; }; var printAndReturn = function(string) { console.log(string); return string; };

Slide 38

Slide 38 text

const greeting = 'hello'; let n = 42; n = 5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz'] function greet() { var g = arguments[0] === undefined ? 'Hi' : arguments[0]; console.log(g); }

Slide 39

Slide 39 text

const greeting = 'hello'; let n = 42; n = 5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz'] function print(a) { var args = [].slice.call(arguments, 1); console.log.apply(console, [a].concat(args)); }

Slide 40

Slide 40 text

const greeting = 'hello'; let n = 42; n = 5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz'] var _ref = ['foo', 'bar', 'baz']; var x = _ref[0]; var y = _ref.slice(1); console.log(x); console.log(y);

Slide 41

Slide 41 text

Declarative vs. Imperative

Slide 42

Slide 42 text

Imperative • Most familiar style of programming • Telling the computer how to accomplish a task • Algorithms are a series of steps • Mutable data usually involved • C, C++, Java

Slide 43

Slide 43 text

Imperative function doubleNumbers(numbers) { const doubled = []; const l = numbers.length; for (let i = 0; i < l; i++) { let doubledNumber = numbers[i] * 2; doubled.push(doubledNumber); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]

Slide 44

Slide 44 text

Imperative function doubleNumbers(numbers) { const doubled = []; const l = numbers.length; for (let i = 0; i < l; i++) { let doubledNumber = numbers[i] * 2; doubled.push(doubledNumber); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]

Slide 45

Slide 45 text

Imperative function doubleNumbers(numbers) { const doubled = []; const l = numbers.length; for (let i = 0; i < l; i++) { let doubledNumber = numbers[i] * 2; doubled.push(doubledNumber); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]

Slide 46

Slide 46 text

Imperative function doubleNumbers(numbers) { const doubled = []; const l = numbers.length; for (let i = 0; i < l; i++) { let doubledNumber = numbers[i] * 2; doubled.push(doubledNumber); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]

Slide 47

Slide 47 text

Imperative function doubleNumbers(numbers) { const doubled = []; const l = numbers.length; for (let i = 0; i < l; i++) { let doubledNumber = numbers[i] * 2; doubled.push(doubledNumber); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]

Slide 48

Slide 48 text

Declarative • Typically associated with functional programming • Telling the computer what you want, not how to get it • Algorithms are compositions of functions • Immutable data (or minimal side effects) preferred • SQL, HTML, Haskell, DSLs

Slide 49

Slide 49 text

Declarative function doubleNumber(n) { return n * 2; } function doubleNumbers(numbers) { return numbers.map(doubleNumber); } doubleNumbers([1, 2, 3]); // [2, 4, 6]

Slide 50

Slide 50 text

First Class Functions Functions are expressions/values.

Slide 51

Slide 51 text

First Class Functions Functions are expressions/values. // Function declaration function add(x, y) { return x + y; } // Assign to a variable const addAlias = add; // Assign an anonymous function expression const multiply = (x, y) => x * y; // Functions have properties console.log(add.name); // 'add' console.log(addAlias.name); // 'add' console.log(multiply.name); // ''

Slide 52

Slide 52 text

First Class Functions Functions are expressions/values. // Function declaration function add(x, y) { return x + y; } // Assign to a variable const addAlias = add; // Assign an anonymous function expression const multiply = (x, y) => x * y; // Functions have properties console.log(add.name); // 'add' console.log(addAlias.name); // 'add' console.log(multiply.name); // ''

Slide 53

Slide 53 text

First Class Functions Functions are expressions/values. // Function declaration function add(x, y) { return x + y; } // Assign to a variable const addAlias = add; // Assign an anonymous function expression const multiply = (x, y) => x * y; // Functions have properties console.log(add.name); // 'add' console.log(addAlias.name); // 'add' console.log(multiply.name); // ''

Slide 54

Slide 54 text

First Class Functions Functions are expressions/values. // Function declaration function add(x, y) { return x + y; } // Assign to a variable const addAlias = add; // Assign an anonymous function expression const multiply = (x, y) => x * y; // Functions have properties console.log(add.name); // 'add' console.log(addAlias.name); // 'add' console.log(multiply.name); // ''

Slide 55

Slide 55 text

First Class Functions Functions as arguments

Slide 56

Slide 56 text

First Class Functions Functions as arguments // Passing in anonymous functions myEventLib.on('update', (data) => console.log(data)); function doubleNumber(n) { return n * 2; } // Earlier example, passing in named function function doubleNumbers(numbers) { return numbers.map(doubleNumber); }

Slide 57

Slide 57 text

First Class Functions Functions as arguments // Passing in anonymous functions myEventLib.on('update', (data) => console.log(data)); function doubleNumber(n) { return n * 2; } // Earlier example, passing in named function function doubleNumbers(numbers) { return numbers.map(doubleNumber); }

Slide 58

Slide 58 text

First Class Functions Functions as return values

Slide 59

Slide 59 text

First Class Functions Functions as return values // Return a new function that adds a number // to another function additionFactory(x) { return (y) => x + y; } const add1 = additionFactory(1); add1(2); // 3

Slide 60

Slide 60 text

Closures

Slide 61

Slide 61 text

Closures • Allow functions to reference other scopes • “Close over” variables in higher scopes • Critical to functional programming paradigms like partial application

Slide 62

Slide 62 text

Closures // New function “closes” over x function additionFactory(x) { return (y) => x + y; } const add1 = additionFactory(1); add1(2); // 3

Slide 63

Slide 63 text

// New function “closes” over x function additionFactory(x) { return (y) => x + y; } const add1 = additionFactory(1); add1(2); // 3 Closures Same x

Slide 64

Slide 64 text

Closures let myValue = 'foo'; function closure() { console.log(myValue); } function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'

Slide 65

Slide 65 text

Closures let myValue = 'foo'; function closure() { console.log(myValue); } function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'

Slide 66

Slide 66 text

let myValue = 'foo'; function closure() { console.log(myValue); } function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar' Closures

Slide 67

Slide 67 text

Closures let myValue = 'foo'; function closure() { console.log(myValue); } function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'

Slide 68

Slide 68 text

Closures let myValue = 'foo'; // Becomes 'hello world' function closure() { console.log(myValue); } function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'

Slide 69

Slide 69 text

Closures let myValue = 'foo'; // Now 'hello world' function closure() { console.log(myValue); } function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'

Slide 70

Slide 70 text

Closures let myValue = 'foo'; // Now 'hello world' function closure() { console.log(myValue); } function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'

Slide 71

Slide 71 text

Referential Transparency

Slide 72

Slide 72 text

Referential Transparency • Fuzzy term associated with purity • For a given input, a function always returns the same value • Replace function calls with return value • No dependency on state outside function

Slide 73

Slide 73 text

Referential Transparency function doubleNumber(n) { return n * 2; } function square(n) { return n * n; } function add6(n) { return n + 6; } // Reduces down to the same value let value1 = add6(square(doubleNumber(3))); // 42 let value2 = add6(square(6)); // 42 let value3 = add6(36); // 42 let value4 = 42; // Same value for every call let otherValue1 = doubleNumber(3) + doubleNumber(3) + doubleNumber(3) + doubleNumber(3); let otherValue2 = 6 + 6 + 6 + 6;

Slide 74

Slide 74 text

Referential Transparency function doubleNumber(n) { return n * 2; } function square(n) { return n * n; } function add6(n) { return n + 6; } // Reduces down to the same value let value1 = add6(square(doubleNumber(3))); // 42 let value2 = add6(square(6)); // 42 let value3 = add6(36); // 42 let value4 = 42; // Same value for every call let otherValue1 = doubleNumber(3) + doubleNumber(3) + doubleNumber(3) + doubleNumber(3); let otherValue2 = 6 + 6 + 6 + 6;

Slide 75

Slide 75 text

Referential Transparency function doubleNumber(n) { return n * 2; } function square(n) { return n * n; } function add6(n) { return n + 6; } // Reduces down to the same value let value1 = add6(square(doubleNumber(3))); // 42 let value2 = add6(square(6)); // 42 let value3 = add6(36); // 42 let value4 = 42; // Same value for every call let otherValue1 = doubleNumber(3) + doubleNumber(3) + doubleNumber(3) + doubleNumber(3); let otherValue2 = 6 + 6 + 6 + 6;

Slide 76

Slide 76 text

Referential Transparency let counter = 0; function addToCounter(n) { counter++; return n + counter; } // Not referentially transparent let value1 = addToCounter(1) + addToCounter(1); // 5 let value2 = 2 + 3; // Not the same value every call!

Slide 77

Slide 77 text

Idempotency

Slide 78

Slide 78 text

Idempotency • Special case of referential transparency • Multiple function applications produce the same result as one application

Slide 79

Slide 79 text

Idempotency let abs = Math.abs; function identity(x) { return x; } function add2(n) { return n + 2; } // Idempotent and referentially transparent abs(abs(abs(abs(-1)))) === abs(-1); identity(identity(identity(42))) === identity(42); // Not idempotent, but referentially transparent add2(add2(add2(1))) !== add2(1); // 7 !== 3

Slide 80

Slide 80 text

Idempotency let abs = Math.abs; function identity(x) { return x; } function add2(n) { return n + 2; } // Idempotent and referentially transparent abs(abs(abs(abs(-1)))) === abs(-1); identity(identity(identity(42))) === identity(42); // Not idempotent, but referentially transparent add2(add2(add2(1))) !== add2(1); // 7 !== 3

Slide 81

Slide 81 text

Idempotency let abs = Math.abs; function identity(x) { return x; } function add2(n) { return n + 2; } // Idempotent and referentially transparent abs(abs(abs(abs(-1)))) === abs(-1); identity(identity(identity(42))) === identity(42); // Not idempotent, but referentially transparent add2(add2(add2(1))) !== add2(1); // 7 !== 3

Slide 82

Slide 82 text

Purity

Slide 83

Slide 83 text

Purity • “Strict referential transparency” • No side effects • No global/shared state • No impure arguments • No I/O

Slide 84

Slide 84 text

Pure Functions function add(x, y) { return x + y; } function capitalize(string) { return string[0].toUpperCase() + string.slice(1).toLowerCase(); } function toTitleCase(string) { return string .split(/\s+/) .map(capitalize) .join(' '); }

Slide 85

Slide 85 text

Impure Functions let myName = 'Jeremy'; const myHobbies = ['programming', 'reading', 'playing guitar']; function getName() { return myName; } function printName(name) { console.log(name); } function add(x, y) { myName = 'Joe'; return x + y; } function hobbiesMapper(hobbies) { return (fn) => hobbies.map(fn); }

Slide 86

Slide 86 text

let myName = 'Jeremy'; const myHobbies = ['programming', 'reading', 'playing guitar']; function getName() { return myName; } function printName(name) { console.log(name); } function add(x, y) { myName = 'Joe'; return x + y; } function hobbiesMapper(hobbies) { return (fn) => hobbies.map(fn); } Impure Functions Accesses global state

Slide 87

Slide 87 text

let myName = 'Jeremy'; const myHobbies = ['programming', 'reading', 'playing guitar']; function getName() { return myName; } function printName(name) { console.log(name); } function add(x, y) { myName = 'Joe'; return x + y; } function hobbiesMapper(hobbies) { return (fn) => hobbies.map(fn); } Impure Functions Prints to stdout

Slide 88

Slide 88 text

let myName = 'Jeremy'; const myHobbies = ['programming', 'reading', 'playing guitar']; function getName() { return myName; } function printName(name) { console.log(name); } function add(x, y) { myName = 'Joe'; return x + y; } function hobbiesMapper(hobbies) { return (fn) => hobbies.map(fn); } Impure Functions Modifies global state

Slide 89

Slide 89 text

let myName = 'Jeremy'; const myHobbies = ['programming', 'reading', 'playing guitar']; function getName() { return myName; } function printName(name) { console.log(name); } function add(x, y) { myName = 'Joe'; return x + y; } function hobbiesMapper(hobbies) { return (fn) => hobbies.map(fn); } Impure Functions Array may mutate

Slide 90

Slide 90 text

Recursion

Slide 91

Slide 91 text

Recursion • Defining a solution to a problem in terms of itself • Solve the problem by solving smaller pieces • Similar to inductive proofs • In programming, a function that calls itself • In FP, imperative looping (e.g. with while or for) is practically nonexistent, so solve with recursion

Slide 92

Slide 92 text

Recursion: Factorial

Slide 93

Slide 93 text

Recursion: Factorial function factorial(n) { let result = 1; while (n > 1) { result *= n--; } return result; } Imperative

Slide 94

Slide 94 text

Recursion: Factorial function factorial(n) { let result = 1; while (n > 1) { result *= n--; } return result; } Imperative function factorial(n) { if (n < 2) { return 1; } return n * factorial(n - 1); } Recursive

Slide 95

Slide 95 text

Recursion: Factorial function factorial(n) { if (n < 2) { return 1; } return n * factorial(n - 1); }

Slide 96

Slide 96 text

Recursion: Factorial function factorial(n) { if (n < 2) { return 1; } return n * factorial(n - 1); } Base case

Slide 97

Slide 97 text

Recursion: Factorial function factorial(n) { if (n < 2) { return 1; } return n * factorial(n - 1); } Recursive call

Slide 98

Slide 98 text

Recursion: Factorial

Slide 99

Slide 99 text

Recursion: Factorial factorial(4);

Slide 100

Slide 100 text

Recursion: Factorial factorial(4); 4 * factorial(3);

Slide 101

Slide 101 text

Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 * factorial(2);

Slide 102

Slide 102 text

Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1);

Slide 103

Slide 103 text

Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1;

Slide 104

Slide 104 text

Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2;

Slide 105

Slide 105 text

Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6;

Slide 106

Slide 106 text

Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24;

Slide 107

Slide 107 text

What about recursion performance?

Slide 108

Slide 108 text

Recursion Performance let value = factorial(100000); console.log(value); // ???

Slide 109

Slide 109 text

Recursion Performance let value = factorial(100000); console.log(value); // ??? RangeError: Maximum call stack size exceeded

Slide 110

Slide 110 text

Recursion: Factorial function factorial(n) { if (n < 2) { return 1; } return n * factorial(n - 1); }

Slide 111

Slide 111 text

Recursion: Factorial function factorial(n) { if (n < 2) { return 1; } return n * factorial(n - 1); }

Slide 112

Slide 112 text

Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24;

Slide 113

Slide 113 text

Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; Stack

Slide 114

Slide 114 text

Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; factorial(4) Stack

Slide 115

Slide 115 text

Recursion: Factorial factorial(4) factorial(3) factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; Stack

Slide 116

Slide 116 text

Recursion: Factorial factorial(4) factorial(3) factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; factorial(2) Stack

Slide 117

Slide 117 text

Recursion: Factorial factorial(4) factorial(3) factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; factorial(2) factorial(1) Stack

Slide 118

Slide 118 text

Recursion: Factorial factorial(4) factorial(3) factorial(2) factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; Stack

Slide 119

Slide 119 text

Recursion: Factorial factorial(4) factorial(3) factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; Stack

Slide 120

Slide 120 text

Recursion: Factorial factorial(4) factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; Stack

Slide 121

Slide 121 text

Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; Stack

Slide 122

Slide 122 text

Recursion Performance let value = factorial(100000); console.log(value); // ??? 100,000 calls = 100,000 stack frames 1 stack frame ≈ 48B Max stack usage ≈ 1MB 100,000 × 48 / 1024 / 1024 = 4.58mb > 1mb

Slide 123

Slide 123 text

Tail call optimization to the rescue!

Slide 124

Slide 124 text

Tail Call Optimization

Slide 125

Slide 125 text

Tail Call Optimization • Optimize recursive functions to not exhaust max stack size • Replace stack frames instead of adding new ones • The last statement before returning must be recursive call • Typically rewrite function to include an accumulator that holds the current result • (Coming to JavaScript in ES2015!)

Slide 126

Slide 126 text

Tail Call Optimization Unoptimizable function factorial(n) { if (n < 2) { return 1; } return n * factorial(n - 1); }

Slide 127

Slide 127 text

Tail Call Optimization Unoptimizable function factorial(n) { if (n < 2) { return 1; } return n * factorial(n - 1); } 1

Slide 128

Slide 128 text

Tail Call Optimization Unoptimizable function factorial(n) { if (n < 2) { return 1; } return n * factorial(n - 1); } 1 2

Slide 129

Slide 129 text

Tail Call Optimization Unoptimizable function factorial(n) { if (n < 2) { return 1; } return n * factorial(n - 1); } 1 3 2

Slide 130

Slide 130 text

Optimize Factorial function factorial(n, accum = 1) { if (n < 2) { return accum; } return factorial(n - 1, n * accum); }

Slide 131

Slide 131 text

Optimize Factorial function factorial(n, accum = 1) { if (n < 2) { return accum; } return factorial(n - 1, n * accum); } Current value accumulator

Slide 132

Slide 132 text

Optimize Factorial function factorial(n, accum = 1) { if (n < 2) { return accum; } return factorial(n - 1, n * accum); } Return the accumulator for base case

Slide 133

Slide 133 text

Optimize Factorial function factorial(n, accum = 1) { if (n < 2) { return accum; } return factorial(n - 1, n * accum); } Move calculation inside the call

Slide 134

Slide 134 text

Optimize Factorial function factorial(n, accum = 1) { if (n < 2) { return accum; } return factorial(n - 1, n * accum); } 1

Slide 135

Slide 135 text

Optimize Factorial function factorial(n, accum = 1) { if (n < 2) { return accum; } return factorial(n - 1, n * accum); } 1 2

Slide 136

Slide 136 text

Optimize Factorial function factorial(n, accum = 1) { if (n < 2) { return accum; } return factorial(n - 1, n * accum); } 1 3 2

Slide 137

Slide 137 text

And now? let value = factorial(100000); console.log(value); // Infinity

Slide 138

Slide 138 text

Factorial TCO factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;

Slide 139

Slide 139 text

Factorial TCO factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24); 24; Stack

Slide 140

Slide 140 text

Factorial TCO factorial(4, 1) Stack factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;

Slide 141

Slide 141 text

Factorial TCO factorial(3, 4) Stack factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;

Slide 142

Slide 142 text

Factorial TCO factorial(2, 12) Stack factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;

Slide 143

Slide 143 text

Factorial TCO factorial(1, 24) Stack factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;

Slide 144

Slide 144 text

Factorial TCO Stack factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;

Slide 145

Slide 145 text

Recursion: Fibonacci

Slide 146

Slide 146 text

Recursion: Fibonacci

Slide 147

Slide 147 text

Recursion: Fibonacci Base cases

Slide 148

Slide 148 text

Recursion: Fibonacci Recurrence

Slide 149

Slide 149 text

Recursion: Fibonacci function fibonacci(n) { if (n < 2) { return n; } return fibonacci(n - 1) + fibonacci(n - 2); }

Slide 150

Slide 150 text

Recursion: Fibonacci function fibonacci(n) { if (n < 2) { return n; } return fibonacci(n - 1) + fibonacci(n - 2); } Base Cases

Slide 151

Slide 151 text

Recursion: Fibonacci function fibonacci(n) { if (n < 2) { return n; } return fibonacci(n - 1) + fibonacci(n - 2); } Recurrence

Slide 152

Slide 152 text

Recursion: Fibonacci fib(0); // 0 fib(1); // 1 fib(2); // 1 fib(3); // 2 fib(4); // 3 fib(5); // 5 function fibonacci(n) { if (n < 2) { return n; } return fibonacci(n - 1) + fibonacci(n - 2); }

Slide 153

Slide 153 text

Performance Revisited fibonacci(1); // 0 ms

Slide 154

Slide 154 text

Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms

Slide 155

Slide 155 text

Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms fibonacci(20); // 0 ms

Slide 156

Slide 156 text

Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms fibonacci(20); // 0 ms fibonacci(30); // 13 ms

Slide 157

Slide 157 text

Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms fibonacci(20); // 0 ms fibonacci(30); // 13 ms fibonacci(35); // 131 ms

Slide 158

Slide 158 text

Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms fibonacci(20); // 0 ms fibonacci(30); // 13 ms fibonacci(35); // 131 ms fibonacci(40); // 1516 ms

Slide 159

Slide 159 text

Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms fibonacci(20); // 0 ms fibonacci(30); // 13 ms fibonacci(35); // 131 ms fibonacci(40); // 1516 ms fibonacci(45); // 16.6 seconds!

Slide 160

Slide 160 text

Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms fibonacci(20); // 0 ms fibonacci(30); // 13 ms fibonacci(35); // 131 ms fibonacci(40); // 1516 ms fibonacci(45); // 16.6 seconds! fibonacci(100); // Who knows how long

Slide 161

Slide 161 text

Exponential Growth Runtime (ms) 0 4500 9000 13500 18000 Nth number of Fibonacci Sequence 0 10 20 30 40 50

Slide 162

Slide 162 text

Recursive Call Tree

Slide 163

Slide 163 text

Recursive Call Tree Computing some of the same values several times!

Slide 164

Slide 164 text

Recursive Call Tree Computing some of the same values several times! fibonacci(4) x 2

Slide 165

Slide 165 text

Recursive Call Tree Computing some of the same values several times! fibonacci(4) x 2 fibonacci(3) x 3

Slide 166

Slide 166 text

Recursive Call Tree Computing some of the same values several times! fibonacci(4) x 2 fibonacci(3) x 3 fibonacci(2) x 5

Slide 167

Slide 167 text

Recursive Call Tree Computing some of the same values several times! fibonacci(4) x 2 fibonacci(3) x 3 fibonacci(2) x 5 fibonacci(1) x 3

Slide 168

Slide 168 text

TCO and Dynamic Programming

Slide 169

Slide 169 text

TCO and Dynamic Programming • Build up the result from the leaves of the tree • Avoids recalculating the same values • Make recursive calls in the reverse direction

Slide 170

Slide 170 text

Fibonacci TCO function fibonacci(n, current = 0, next = 1) { if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); }

Slide 171

Slide 171 text

Fibonacci TCO function fibonacci(n, current = 0, next = 1) { if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); } Two accumulators

Slide 172

Slide 172 text

Fibonacci TCO function fibonacci(n, current = 0, next = 1) { if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); } Results for next call

Slide 173

Slide 173 text

Fibonacci TCO function fibonacci(n, current = 0, next = 1) { if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); } Reach the end, so return whatever was built up

Slide 174

Slide 174 text

Fibonacci TCO fibonacci(6 /*, 0, 1 */); fibonacci(5, 1, 1); fibonacci(4, 1, 2); fibonacci(3, 2, 3); fibonacci(2, 3, 5); fibonacci(1, 5, 8); fibonacci(0, 8, 13); 8;

Slide 175

Slide 175 text

Fibonacci TCO fibonacci(6 /*, 0, 1 */); fibonacci(5, 1, 1); fibonacci(4, 1, 2); fibonacci(3, 2, 3); fibonacci(2, 3, 5); fibonacci(1, 5, 8); fibonacci(0, 8, 13); 8; Fibonacci sequence

Slide 176

Slide 176 text

Fibonacci Performance fibonacci(1); // 0 ms fibonacci(5); // 0 ms fibonacci(20); // 0 ms fibonacci(30); // 0 ms fibonacci(35); // 0 ms fibonacci(40); // 0 ms fibonacci(45); // 0 ms fibonacci(100); // 0 ms

Slide 177

Slide 177 text

Partial Application

Slide 178

Slide 178 text

Partial Application • Assign values to function parameters without evaluating the function (i.e. “prefill” argument values) • Produce a new function that takes in any remaining unassigned arguments

Slide 179

Slide 179 text

Partial Application // Before we would use this function additionFactory(x) { return (y) => x + y; } let add1 = additionFactory(1); add1(2); // 3 // Now we can write an add function // and use partial application function add(x, y) { return x + y; } // Partial application with native `bind` function add1 = add.bind(null, 1); add1(2); // 3 // Use a custom-written `partial` function add1 = partial(add, 1); add1(2); // 3

Slide 180

Slide 180 text

Partial Application // Before we would use this function additionFactory(x) { return (y) => x + y; } let add1 = additionFactory(1); add1(2); // 3 // Now we can write an add function // and use partial application function add(x, y) { return x + y; } // Partial application with native `bind` function add1 = add.bind(null, 1); add1(2); // 3 // Use a custom-written `partial` function add1 = partial(add, 1); add1(2); // 3

Slide 181

Slide 181 text

Partial Application // Before we would use this function additionFactory(x) { return (y) => x + y; } let add1 = additionFactory(1); add1(2); // 3 // Now we can write an add function // and use partial application function add(x, y) { return x + y; } // Partial application with native `bind` function add1 = add.bind(null, 1); add1(2); // 3 // Use a custom-written `partial` function add1 = partial(add, 1); add1(2); // 3

Slide 182

Slide 182 text

Partial Application // Before we would use this function additionFactory(x) { return (y) => x + y; } let add1 = additionFactory(1); add1(2); // 3 // Now we can write an add function // and use partial application function add(x, y) { return x + y; } // Partial application with native `bind` function add1 = add.bind(null, 1); add1(2); // 3 // Use a custom-written `partial` function add1 = partial(add, 1); add1(2); // 3

Slide 183

Slide 183 text

Partial Application function partial(fn, ...args) { return (...otherArgs) => { return fn(...args, ...otherArgs); }; }

Slide 184

Slide 184 text

Partial Application function partial(fn, ...args) { return (...otherArgs) => { return fn(...args, ...otherArgs); }; } Applied arguments

Slide 185

Slide 185 text

Partial Application function partial(fn, ...args) { return (...otherArgs) => { return fn(...args, ...otherArgs); }; } Return new closure

Slide 186

Slide 186 text

Partial Application function partial(fn, ...args) { return (...otherArgs) => { return fn(...args, ...otherArgs); }; } Remaining arguments

Slide 187

Slide 187 text

Partial Application function partial(fn, ...args) { return (...otherArgs) => { return fn(...args, ...otherArgs); }; } Call original function with all arguments

Slide 188

Slide 188 text

Partial Application: Why? • Build up functions from smaller pieces • Remove duplication • Generalize function abstraction (i.e. no additionFactory type functions)

Slide 189

Slide 189 text

Partial Application: Why? function multiply(x, y) { return x * y; } const doubleNumber = partial(multiply, 2); const numbers = [1, 2, 3]; const doubledNumbers = map(doubleNumber, numbers); console.log(doubledNumbers); // [2, 4, 6]

Slide 190

Slide 190 text

Currying

Slide 191

Slide 191 text

Currying • Similar to partial application • Bakes partial application into a function • Successive invocations of function with arguments returns a new function with those arguments assigned • Keep returning a new function until all arguments “filled,” and then invoke the actual function with all of those arguments (sounds recursive, huh?)

Slide 192

Slide 192 text

Currying const add = curry((x, y, z) => x + y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6

Slide 193

Slide 193 text

Currying const add = curry((x, y, z) => x + y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6

Slide 194

Slide 194 text

Currying const add = curry((x, y, z) => x + y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6

Slide 195

Slide 195 text

Currying const add = curry((x, y, z) => x + y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6

Slide 196

Slide 196 text

Currying const add = curry((x, y, z) => x + y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6

Slide 197

Slide 197 text

Currying const add = curry((x, y, z) => x + y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6

Slide 198

Slide 198 text

Currying function curry(fn, len = fn.length) { return (...args) => { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; }

Slide 199

Slide 199 text

Currying function curry(fn, len = fn.length) { return (...args) => { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Knowing arity is important!

Slide 200

Slide 200 text

Currying function curry(fn, len = fn.length) { return (...args) => { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Arguments to apply (or invoke)

Slide 201

Slide 201 text

Currying function curry(fn, len = fn.length) { return (...args) => { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Recursive call if not all arguments supplied

Slide 202

Slide 202 text

Currying function curry(fn, len = fn.length) { return (...args) => { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Curry the partially applied function

Slide 203

Slide 203 text

Currying function curry(fn, len = fn.length) { return (...args) => { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Arity decreases

Slide 204

Slide 204 text

Currying function curry(fn, len = fn.length) { return (...args) => { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Base case: all arguments supplied

Slide 205

Slide 205 text

Currying: Why? let curriedMap = curry(map); let multiply = curry((x, y) => x * y); let doubleNumbers = curriedMap(multiply(2)); let numbers = [1, 2, 3]; console.log(doubleNumbers(numbers)); // [2, 4, 6]

Slide 206

Slide 206 text

Function Composition

Slide 207

Slide 207 text

Function Composition

Slide 208

Slide 208 text

Function Composition • Compose functions together to form new functions • Pipe function output to next function • Helps enforce modularity/ SOC by keeping functions small

Slide 209

Slide 209 text

Function Composition let multiply = curry((x, y) => x * y); let multiply2 = multiply(2); let multiply3 = multiply(3); let multiply6 = compose(multiply2, multiply3); 2 * 3 * 6 === multiply6(6); // 36 let add = curry((x, y) => x + y); let subtract = curry((y, x) => -y + x); let add5 = compose(add(6), add(1), subtract(2)); 3 - 2 + 6 + 1 === add5(3); // 8

Slide 210

Slide 210 text

Function Composition let multiply = curry((x, y) => x * y); let multiply2 = multiply(2); let multiply3 = multiply(3); let multiply6 = compose(multiply2, multiply3); 2 * 3 * 6 === multiply6(6); // 36 let add = curry((x, y) => x + y); let subtract = curry((y, x) => -y + x); let add5 = compose(add(6), add(1), subtract(2)); 3 - 2 + 6 + 1 === add5(3); // 8

Slide 211

Slide 211 text

Function Composition let multiply = curry((x, y) => x * y); let multiply2 = multiply(2); let multiply3 = multiply(3); let multiply6 = compose(multiply2, multiply3); 2 * 3 * 6 === multiply6(6); // 36 let add = curry((x, y) => x + y); let subtract = curry((y, x) => -y + x); let add5 = compose(add(6), add(1), subtract(2)); 3 - 2 + 6 + 1 === add5(3); // 8

Slide 212

Slide 212 text

Function Composition function compose(...fns) { return (...args) => { const result = fns.reduceRight((memo, fn) => { return [fn(...memo)]; }, args); return result[0]; }; } function compose(f, g) { return (...args) => f(g(...args)); }

Slide 213

Slide 213 text

Function Composition function compose(...fns) { return (...args) => { const result = fns.reduceRight((memo, fn) => { return [fn(...memo)]; }, args); return result[0]; }; } function compose(f, g) { return (...args) => f(g(...args)); }

Slide 214

Slide 214 text

Function Composition function compose(...fns) { return (...args) => { const result = fns.reduceRight((memo, fn) => { return [fn(...memo)]; }, args); return result[0]; }; } function compose(f, g) { return (...args) => f(g(...args)); }

Slide 215

Slide 215 text

Function Composition function compose(...fns) { return (...args) => { const result = fns.reduceRight((memo, fn) => { return [fn(...memo)]; }, args); return result[0]; }; } function compose(f, g) { return (...args) => f(g(...args)); }

Slide 216

Slide 216 text

Function Composition function compose(...fns) { return (...args) => { const result = fns.reduceRight((memo, fn) => { return [fn(...memo)]; }, args); return result[0]; }; } function compose(f, g) { return (...args) => f(g(...args)); }

Slide 217

Slide 217 text

Slide 218

Slide 218 text

But why Functional Programming?

Slide 219

Slide 219 text

Why FP • Concise, elegant solutions to problems • Modularity and composition • Eliminate data races with immutability • Unit tests are simpler — no checking for state changes

Slide 220

Slide 220 text

• Prof. Frisby’s Mostly Adequate…
 (github.com/MostlyAdequate/mostly-adequate-guide) • Babel (babeljs.io) • Lodash (lodash.com) • Ramda (ramdajs.com) • Clojurescript (github.com/clojure/clojurescript) • Immutable.js (github.com/facebook/immutable-js) • React (facebook.github.io/react/) • Redux (redux.js.org)

Slide 221

Slide 221 text

Thanks! jfairbank @elpapapollo blog.jeremyfairbank.com Code samples (some ES5 too) github.com/jfairbank/functional-javascript