Jeremy Fairbank
July 11, 2015
470

# CodeStock: Functional Programming Basics in JavaScript

July 11, 2015

## Transcript

1. Functional Programming Basics in JavaScript
2:40 PM / Ballroom A

2. Functional Programming Basics
in JavaScript
Jeremy Fairbank
@elpapapollo

3. Understand, apply, and create basic
functional programming tenets and
constructs in JavaScript.

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

5. What is a function?

6. What is a function?

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

8. What is a function?

9. What is a function?

10. What is a function?

11. What is a function?
Domain

12. What is a function?
Domain Range

13. Unique mapping from input to output!
Domain Range

14. Ugh, math?

15. Alonzo Church

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

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

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

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

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

21. λ-Calculus
Functions are anonymous.

22. Functions are anonymous.
λ-Calculus

23. Functions are anonymous.
λ-Calculus
X

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

25. λ-Calculus
Functions are single input.

26. Functions are single input.
λ-Calculus

27. Functions are single input.
λ-Calculus

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

29. That’s nice.
programming?

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

31. What is Functional
Programming?

32. – 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 is done with expressions.”
What is Functional Programming?

33. TL;DR
Programming without assignment statements.

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

36. 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. 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']

38. 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']

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']
var add = function(x, y) {
return x + y;
};
var printAndReturn = function(string) {
console.log(string);
return string;
};

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']
function greet() {
var g = arguments[0] === undefined ? 'Hi' : arguments[0];
console.log(g);
}

41. 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));
}

42. 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);

43. Declarative vs. Imperative

44. 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

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. 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]

49. 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]

50. 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

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

52. First Class Functions
Functions are expressions/values.

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

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

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

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

57. First Class Functions
Functions as arguments

58. 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);
}

59. 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);
}

60. First Class Functions
Functions as return values

61. First Class Functions
Functions as return values
// Return a new function that adds a number
// to another
return (y) => x + y;
}

62. First Class Functions
Functions as return values
// Return a new function that adds a number
// to another
return (y) => x + y;
}

63. First Class Functions
Functions as return values
// Return a new function that adds a number
// to another
return (y) => x + y;
}

64. Closures

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

66. Closures
// New function “closes” over x
return (y) => x + y;
}

67. // New function “closes” over x
return (y) => x + y;
}
Closures
Same x

68. 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'

69. 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'

70. 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

71. 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'

72. 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'

73. 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'

74. 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'

75. Referential Transparency

76. 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

77. Referential
Transparency
function doubleNumber(n) {
return n * 2;
}
function square(n) {
return n * 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;

78. Referential
Transparency
function doubleNumber(n) {
return n * 2;
}
function square(n) {
return n * 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;

79. Referential
Transparency
function doubleNumber(n) {
return n * 2;
}
function square(n) {
return n * 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;

80. Referential
Transparency
let counter = 0;
counter++;
return n + counter;
}
let counter = 0;
counter++;
return n + counter;
}
// Not referentially transparent
let value2 = 2 + 3; // Not the same value every call!
// Referentially transparent
let otherValue2 = 2 + 2;

81. Referential
Transparency
let counter = 0;
counter++;
return n + counter;
}
let counter = 0;
counter++;
return n + counter;
}
// Not referentially transparent
let value2 = 2 + 3; // Not the same value every call!
// Referentially transparent
let otherValue2 = 2 + 2;

82. Referential
Transparency
let counter = 0;
counter++;
return n + counter;
}
let counter = 0;
counter++;
return n + counter;
}
// Not referentially transparent
let value2 = 2 + 3; // Not the same value every call!
// Referentially transparent
let otherValue2 = 2 + 2;

83. Idempotency

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

85. Idempotency
let abs = Math.abs;
function identity(x) {
return x;
}
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

86. Idempotency
let abs = Math.abs;
function identity(x) {
return x;
}
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

87. Idempotency
let abs = Math.abs;
function identity(x) {
return x;
}
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

88. Purity

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

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

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

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

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

94. Pure Functions
return x + y;
}
function capitalize(string) {
return string[0].toUpperCase() +
string.slice(1).toLowerCase();
}
function toTitleCase(string) {
return string
.split(/\s+/)
.map(capitalize)
.join(' ');
}
• No side effects.
• String and number
arguments are immutable.

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

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

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

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

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

100. Recursion

101. 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

102. Recursion: Factorial

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

104. 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

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

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

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

108. Recursion: Factorial

109. Recursion: Factorial
factorial(4);

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

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

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

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

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

115. 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;

116. 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;

performance?

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

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

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

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

122. 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;

123. 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

124. 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

125. 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

126. 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

127. 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

128. 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

129. 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

130. 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

131. 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

132. 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

133. Tail call optimization to the
rescue!

134. Tail Call Optimization

135. Tail Call Optimization
• Optimize recursive functions to not exhaust max stack
size
• 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!)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Stack

150. Factorial TCO

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

151. Factorial TCO

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

152. Factorial TCO

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

153. Factorial TCO

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

154. Factorial TCO

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

155. Recursion: Fibonacci

156. Recursion: Fibonacci

157. Recursion: Fibonacci
Base
cases

158. Recursion: Fibonacci
Recurrence

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

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

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

162. 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);
}

163. Performance Revisited
fibonacci(1); // 0 ms

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

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

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

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

168. 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

169. 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!

170. 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

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

172. Recursive Call Tree

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

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

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

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

177. 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

178. TCO and Dynamic Programming

179. 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

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

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

182. 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

183. 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

184. 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;

185. 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

186. 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

187. Functional Arrays

188. Functional Arrays
• Common operations like map can be implemented in FP
• Enforce immutability by returning new array
• No looping; use recursion!

189. Array#map
Map values in array to other values in new array
function map(fn, array) {
if (array.length === 0) {
return array;
}
}

190. Array#map
Map values in array to other values in new array
function map(fn, array) {
if (array.length === 0) {
return array;
}
}
Function ﬁrst; good
for currying

191. Array#map
Map values in array to other values in new array
function map(fn, array) {
if (array.length === 0) {
return array;
}
}
Grab the ﬁrst element
and remaining elements

192. function map(fn, array) {
if (array.length === 0) {
return array;
}
}
Array#map
Map values in array to other values in new array

193. function map(fn, array) {
if (array.length === 0) {
return array;
}
}
Array#map
Map values in array to other values in new array
var tail = array.slice(1);

194. Array#map
Map values in array to other values in new array
function map(fn, array) {
if (array.length === 0) {
return array;
}
}
Return new array with
array of remaining elements

195. Array#map
Map values in array to other values in new array
function map(fn, array) {
if (array.length === 0) {
return array;
}
}

196. Array#map
Map values in array to other values in new array
function map(fn, array) {
if (array.length === 0) {
return array;
}
}
Base case: empty array.

197. Array#map
Map values in array to other values in new array
function square(n) {
return n * n;
}
let numbers = [1, 2, 3];
let squaredNumbers = map(square, numbers);
// [1, 4, 9];

198. Partial Application

199. 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

200. Partial
Application
// Before we would use this
return (y) => x + y;
}
// Now we can write an add function
// and use partial application
return x + y;
}
// Partial application with native `bind` function
// Use a custom-written `partial` function

201. Partial
Application
// Before we would use this
return (y) => x + y;
}
// Now we can write an add function
// and use partial application
return x + y;
}
// Partial application with native `bind` function
// Use a custom-written `partial` function

202. Partial
Application
// Before we would use this
return (y) => x + y;
}
// Now we can write an add function
// and use partial application
return x + y;
}
// Partial application with native `bind` function
// Use a custom-written `partial` function

203. Partial
Application
// Before we would use this
return (y) => x + y;
}
// Now we can write an add function
// and use partial application
return x + y;
}
// Partial application with native `bind` function
// Use a custom-written `partial` function

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

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

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

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

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

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

210. 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]

211. Currying

212. 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?)

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

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

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

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

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

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

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

220. 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!

221. 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)

222. 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

223. 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

224. 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

225. 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

226. 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]

227. Function Composition

228. Function Composition

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

230. 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);
3 - 2 + 6 + 1 === add5(3); // 8

231. 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);
3 - 2 + 6 + 1 === add5(3); // 8

232. 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);
3 - 2 + 6 + 1 === add5(3); // 8

233. 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));
}

234. 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));
}

235. 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));
}

236. 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));
}

237. 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));
}

238. But why Functional
Programming?

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

240. Extra Resources
• Babel (babeljs.io)
• Lodash (lodash.com)
• Clojurescript (github.com/clojure/clojurescript)