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'
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
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
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
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
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)
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