Jeremy Fairbank
October 16, 2015
200

# ConnectJS Functional Programming Basics in ES6

October 16, 2015

## Transcript

5. ### What is a function? Relation that pairs each element in

the domain with exactly one element in the range. Domain Range

14. ### Alonzo Church • Church-Turing Thesis • Lambda Calculus • Represent

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

using variable binding and substitution.”
16. ### Represent computation in terms of “function abstraction and application using

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

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

variable binding and substitution.” λ-Calculus Substitution => apply arguments

computation.

— currying!

28. ### λ-Calculus shows the power of functional computation. Functional programming brings

it to life in computer science!

30. ### – 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?

32. ### Key Concepts • Declarative (vs. Imperative) • First Class Functions

• Referential Transparency and Purity • Recursion • Immutability • Partial Application and Currying • Composition

34. ### 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']
35. ### 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']
36. ### 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']
37. ### 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; };
38. ### 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); }
39. ### 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)); }
40. ### 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);

42. ### 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
43. ### 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]
44. ### 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]
45. ### 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]
46. ### 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]
47. ### 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]
48. ### 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
49. ### Declarative function doubleNumber(n) { return n * 2; } function

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

51. ### 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); // ''
52. ### 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); // ''
53. ### 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); // ''
54. ### 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); // ''

56. ### 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); }
57. ### 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); }

61. ### Closures • Allow functions to reference other scopes • “Close

over” variables in higher scopes • Critical to functional programming paradigms like partial application

64. ### 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'
65. ### 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'
66. ### 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
67. ### 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'
68. ### 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'
69. ### 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'
70. ### 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'

72. ### 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
73. ### 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;
74. ### 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;
75. ### 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;
76. ### 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!

78. ### Idempotency • Special case of referential transparency • Multiple function

applications produce the same result as one application
79. ### 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
80. ### 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
81. ### 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

83. ### Purity • “Strict referential transparency” • No side effects •

No global/shared state • No impure arguments • No I/O
84. ### 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(' '); }
85. ### 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); }
86. ### 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
87. ### 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
88. ### 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
89. ### 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

91. ### 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

93. ### Recursion: Factorial function factorial(n) { let result = 1; while

(n > 1) { result *= n--; } return result; } Imperative
94. ### 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
95. ### Recursion: Factorial function factorial(n) { if (n < 2) {

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

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

return 1; } return n * factorial(n - 1); } Recursive call

101. ### Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 *

factorial(2);
102. ### Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 *

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

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

factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2;
105. ### 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;
106. ### 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;

109. ### Recursion Performance let value = factorial(100000); console.log(value); // ??? RangeError:

Maximum call stack size exceeded
110. ### Recursion: Factorial function factorial(n) { if (n < 2) {

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

return 1; } return n * factorial(n - 1); }
112. ### 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;
113. ### 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; <main> Stack
114. ### 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; <main> factorial(4) Stack
115. ### Recursion: Factorial <main> 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
116. ### Recursion: Factorial <main> 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
117. ### Recursion: Factorial <main> 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
118. ### Recursion: Factorial <main> 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
119. ### Recursion: Factorial <main> 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
120. ### Recursion: Factorial <main> 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
121. ### Recursion: Factorial <main> 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
122. ### 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

125. ### 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!)
126. ### Tail Call Optimization Unoptimizable function factorial(n) { if (n <

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

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

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

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

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

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

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

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

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

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

< 2) { return accum; } return factorial(n - 1, n * accum); } 1 3 2

138. ### Factorial TCO factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12);

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

factorial(1, 24); 24; <main> Stack
140. ### Factorial TCO <main> factorial(4, 1) Stack factorial(4 /*, 1 */);

factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;
141. ### Factorial TCO <main> factorial(3, 4) Stack factorial(4 /*, 1 */);

factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;
142. ### Factorial TCO <main> factorial(2, 12) Stack factorial(4 /*, 1 */);

factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;
143. ### Factorial TCO <main> factorial(1, 24) Stack factorial(4 /*, 1 */);

factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;
144. ### Factorial TCO <main> Stack factorial(4 /*, 1 */); factorial(3, 4);

factorial(2, 12); factorial(1, 24); 24;

149. ### Recursion: Fibonacci function fibonacci(n) { if (n < 2) {

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

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

return n; } return fibonacci(n - 1) + fibonacci(n - 2); } Recurrence
152. ### 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); }

155. ### Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms

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

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

fibonacci(20); // 0 ms fibonacci(30); // 13 ms fibonacci(35); // 131 ms
158. ### 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
159. ### 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!
160. ### 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
161. ### Exponential Growth Runtime (ms) 0 4500 9000 13500 18000 Nth

number of Fibonacci Sequence 0 10 20 30 40 50

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

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

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

times! fibonacci(4) x 2 fibonacci(3) x 3 fibonacci(2) x 5
167. ### 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

169. ### 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
170. ### Fibonacci TCO function fibonacci(n, current = 0, next = 1)

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

{ if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); } Two accumulators
172. ### 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
173. ### 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
174. ### 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;
175. ### 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
176. ### 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

178. ### 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

183. ### Partial Application function partial(fn, ...args) { return (...otherArgs) => {

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

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

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

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

return fn(...args, ...otherArgs); }; } Call original function with all arguments
188. ### Partial Application: Why? • Build up functions from smaller pieces

• Remove duplication • Generalize function abstraction (i.e. no additionFactory type functions)
189. ### 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]

191. ### 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?)
192. ### 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
193. ### 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
194. ### 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
195. ### 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
196. ### 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
197. ### 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
198. ### Currying function curry(fn, len = fn.length) { return (...args) =>

{ if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; }
199. ### 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!
200. ### 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)
201. ### 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
202. ### 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
203. ### 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
204. ### 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
205. ### 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]

208. ### Function Composition • Compose functions together to form new functions

• Pipe function output to next function • Helps enforce modularity/ SOC by keeping functions small
209. ### 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
210. ### 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
211. ### 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
212. ### 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)); }
213. ### 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)); }
214. ### 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)); }
215. ### 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)); }
216. ### 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)); }

219. ### Why FP • Concise, elegant solutions to problems • Modularity

and composition • Eliminate data races with immutability • Unit tests are simpler — no checking for state changes