Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Scenic City Summit: Functional Programming Basi...
Search
Jeremy Fairbank
August 12, 2016
Programming
6
1.1k
Scenic City Summit: Functional Programming Basics in ES6
Jeremy Fairbank
August 12, 2016
Tweet
Share
More Decks by Jeremy Fairbank
See All by Jeremy Fairbank
Connect.Tech 2020: Advanced Cypress Testing
jfairbank
1
180
CodeMash 2020: Solving the Boolean Identity Crisis
jfairbank
1
130
CodeMash 2020: Practical Functional Programming
jfairbank
1
290
Connect.Tech 2019: Practical Functional Programming
jfairbank
0
310
Connect.Tech 2019: Solving the Boolean Identity Crisis
jfairbank
0
160
Lambda Squared 2019: Solving the Boolean Identity Crisis
jfairbank
0
98
All Things Open 2018: Practical Functional Programming
jfairbank
2
250
Connect.Tech 2018: Effective React Testing
jfairbank
1
130
Fluent Conf 2018: Building web apps with Elm Tutorial
jfairbank
2
800
Other Decks in Programming
See All in Programming
3 Effective Rules for Using Signals in Angular
manfredsteyer
PRO
0
100
.NET のための通信フレームワーク MagicOnion 入門 / Introduction to MagicOnion
mayuki
1
1.4k
CSC509 Lecture 09
javiergs
PRO
0
140
Nurturing OpenJDK distribution: Eclipse Temurin Success History and plan
ivargrimstad
0
870
OnlineTestConf: Test Automation Friend or Foe
maaretp
0
100
Kaigi on Rails 2024 〜運営の裏側〜
krpk1900
1
190
ピラミッド、アイスクリームコーン、SMURF: 自動テストの最適バランスを求めて / Pyramid Ice-Cream-Cone and SMURF
twada
PRO
10
1.3k
광고 소재 심사 과정에 AI를 도입하여 광고 서비스 생산성 향상시키기
kakao
PRO
0
170
ペアーズにおけるAmazon Bedrockを⽤いた障害対応⽀援 ⽣成AIツールの導⼊事例 @ 20241115配信AWSウェビナー登壇
fukubaka0825
6
1.8k
PHP でアセンブリ言語のように書く技術
memory1994
PRO
1
170
距離関数を極める! / SESSIONS 2024
gam0022
0
280
Laravel や Symfony で手っ取り早く OpenAPI のドキュメントを作成する
azuki
1
110
Featured
See All Featured
We Have a Design System, Now What?
morganepeng
50
7.2k
Gamification - CAS2011
davidbonilla
80
5k
The Pragmatic Product Professional
lauravandoore
31
6.3k
The World Runs on Bad Software
bkeepers
PRO
65
11k
The Cult of Friendly URLs
andyhume
78
6k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
10
720
Build The Right Thing And Hit Your Dates
maggiecrowley
33
2.4k
Faster Mobile Websites
deanohume
305
30k
Put a Button on it: Removing Barriers to Going Fast.
kastner
59
3.5k
Rails Girls Zürich Keynote
gr2m
94
13k
Fireside Chat
paigeccino
34
3k
A Tale of Four Properties
chriscoyier
156
23k
Transcript
FUNCTIONAL PROGRAMMING Jeremy Fairbank blog.jeremyfairbank.com @elpapapollo / jfairbank BASICS IN
ES6
sigient.com Your website, SimplyBuilt. simplybuilt.com
None
¯\_(ϑ)_/¯ WHAT IS FUNCTIONAL PROGRAMMING?
¯\_(ϑ)_/¯ WHY FUNCTIONAL PROGRAMMING?
None
Domain to Range
None
None
Domain Range
CALCULUS
PRINCIPLES
PURE & DECLARATIVE PREDICTABLE
IMMUTABLE STATE SAFE
FIRST CLASS STATE TRANSPARENT
COMPOSABLE FIRST CLASS CLOSURES MODULAR
ES2015 (ES6)
All good let age = 28; age = 29; const
name = 'Jeremy'; name = 'Jet'; Syntax error
const add = (x, y) => { return x +
y; }; const identity = x => x;
const add = (x, y) => { return x +
y; }; const identity = x => x;
const array = (...elements) => { return elements; }; array(1,
2, 3); // [1, 2, 3] const log = (...args) => { console.log(...args); }; log('hello', 'scenic city summit'); // hello scenic city summit
const array = (...elements) => { return elements; }; array(1,
2, 3); // [1, 2, 3] const log = (...args) => { console.log(...args); }; log('hello', 'scenic city summit'); // hello scenic city summit
const langs = ['JavaScript', 'Ruby', 'Haskell']; const [js, ...rest] =
langs; js === 'JavaScript'; rest[0] === 'Ruby'; rest[1] === 'Haskell'; const head = ([x]) => x; head([1, 2, 3]) === 1;
const langs = ['JavaScript', 'Ruby', 'Haskell']; const [js, ...rest] =
langs; js === 'JavaScript'; rest[0] === 'Ruby'; rest[1] === 'Haskell'; const head = ([x]) => x; head([1, 2, 3]) === 1;
const greet = (name, greeting = 'Hi') => { console.log(greeting,
name); }; greet('Scenic City Summit', 'Hello'); // Hello Scenic City Summit greet('Chattanooga'); // Hi Chattanooga
Object.assign( {}, { hello: 'Chattanooga' }, { hi: 'Scenic City
Summit' } ); // { // hello: 'Chattanooga', // hi: 'Scenic City Summit' // }
class Point { constructor(x, y) { this.x = x; this.y
= y; } moveBy(dx, dy) { this.x += dx; this.y += dy; } } function Point(x, y) { this.x = x; this.y = y; } Point.prototype.moveBy = function(dx, dy) { this.x += dx; this.y += dy; };
PURE
const add = (x, y) => x + y; add(2,
3) === 5; add(2, 3) === 5; add(2, 3) === 5;
const add = (x, y) => x + y; add(2,
3) === 5; add(2, 3) === 5; add(2, 3) === 5; Referentially transparent
× let name = 'Jeremy'; const getName = () =>
name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
describe('api', () => { beforeEach(() => mockConsoleLog()); afterEach(() => restoreConsoleLog());
it('sets and prints the name', () => { printUpperName(); expect(console.log).calledWith('JEREMY'); setName('Jet'); printUpperName(); expect(console.log).calledWith('JET'); }); }); ×
HIDDEN STATE IS UNCERTAIN STATE
const upperName = (name) => name.toUpperCase(); describe('api', () => {
it('returns an uppercase name', () => { expect(upperName('Jeremy')).to.equal('JEREMY'); expect(upperName('Jet')).to.equal('JET'); }); }); ✓
HOW TO ACHIEVE THE RESULT IMPERATIVE
function doubleNumbers(numbers) { const doubled = []; const l =
numbers.length; for (let i = 0; i < l; i++) { doubled.push(numbers[i] * 2); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]
DECLARE WHAT THE DESIRED RESULT IS DECLARATIVE
function doubleNumbers(numbers) { return numbers.map(n => n * 2); }
doubleNumbers([1, 2, 3]); // [2, 4, 6]
1 2 3 numbers.map(n => n * 2) 2 4
6 Domain Range
CREATE STATE, DON’T MUTATE IT IMMUTABLE
const hobbies = [ 'programming', 'reading', 'music' ]; const firstTwo
= hobbies.splice(0, 2); console.log(firstTwo); // ['programming', 'reading'] console.log(hobbies); // ['music']
const hobbies = [ 'programming', 'reading', 'music' ]; const firstTwo
= hobbies.splice(0, 2); console.log(firstTwo); // ['programming', 'reading'] console.log(hobbies); // ['music'] Slight typo + mutability
Object.freeze Immutable.js
const hobbies = Object.freeze([ 'programming', 'reading', 'music' ]); const firstTwo
= hobbies.splice(0, 2); // TypeError
FREE YOUR STATE
class Point { constructor(x, y) { this.x = x; this.y
= y; } moveBy(dx, dy) { this.x += dx; this.y += dy; } } const point = new Point(0, 0); point.moveBy(5, 5); point.moveBy(-2, 2); console.log([point.x, point.y]); // [3, 7] ×
const createPoint = (x, y) => Object.freeze([x, y]); const movePointBy
= ([x, y], dx, dy) => { return Object.freeze([x + dx, y + dy]); }; let point = createPoint(0, 0); point = movePointBy(point, 5, 5); point = movePointBy(point, -2, 2); console.log(point); // [3, 7] ✓ (*or object-oriented without mutation)
PROS SAFETY FREE UNDO/REDO LOGS — REDUX EXPLICIT FLOW OF
DATA LESS MEMORY USAGE* CONCURRENCY SAFETY* *In certain cases
CONS VERBOSE MORE OBJECT CREATION* MORE GARBAGE COLLECTION* MORE MEMORY
USAGE* *Alleviated with libs like Immutable.js
FIRST CLASS FUNCTIONS
const multiply = (x, y) => x * y; function
add(x, y) { return x + y; } const addAlias = add; const evens = [1, 2, 3].map(n => n * 2);
const multiply = (x, y) => x * y; function
add(x, y) { return x + y; } const addAlias = add; const evens = [1, 2, 3].map(n => n * 2);
const multiply = (x, y) => x * y; function
add(x, y) { return x + y; } const addAlias = add; const evens = [1, 2, 3].map(n => n * 2);
const multiply = (x, y) => x * y; function
add(x, y) { return x + y; } const addAlias = add; const evens = [1, 2, 3].map(n => n * 2);
ENCAPSULATION CLOSURES
const createAdder = (x) => { return (y) => x
+ y; }; const add3 = createAdder(3); add3(2) === 5; add3(3) === 6;
const createAdder = (x) => { return (y) => x
+ y; }; const add3 = createAdder(3); add3(2) === 5; add3(3) === 6;
const request = (options) => { return fetch(options.url, options) .then(resp
=> resp.json()); }; const usersPromise = request({ url: '/users', headers: { 'X-Custom': 'mykey' } }); const tasksPromise = request({ url: '/tasks', headers: { 'X-Custom': 'mykey' } });
const request = (options) => { return fetch(options.url, options) .then(resp
=> resp.json()); }; const usersPromise = request({ url: '/users', headers: { 'X-Custom': 'mykey' } }); const tasksPromise = request({ url: '/tasks', headers: { 'X-Custom': 'mykey' } }); Repetitive
const createRequester = (options) => { return (otherOptions) => {
return request(Object.assign( {}, options, otherOptions )); }; }; const customRequest = createRequester({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
const createRequester = (options) => { return (otherOptions) => {
return request(Object.assign( {}, options, otherOptions )); }; }; const customRequest = createRequester({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
FOUNDATION FOR HIGHER ORDER PATTERNS FIRST CLASS CLOSURES
PARTIAL APPLICATION
const createAdder = (x) => { return (y) => x
+ y; }; const createRequester = (options) => { return (otherOptions) => { return request(Object.assign( {}, options, otherOptions )); }; }; RECALL
const add = (x, y) => x + y; const
add3 = partial(add, 3); add3(2) === 5;
const request = (defaults, options) => { options = Object.assign({},
defaults, options); return fetch(options.url, options) .then(resp => resp.json()); }; const customRequest = partial(request, { headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);
}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);
}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);
}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);
}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);
}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);
}; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
CURRYING
const add3 = add(3); add3(2) === 5; const customRequest =
request({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
const add3 = add(3); add3(2) === 5; const customRequest =
request({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
const add = x => y => x + y;
function add(x) { return function(y) { return x + y; }; }
const add = x => y => x + y;
function add(x) { return function(y) { return x + y; }; }
const add = x => y => x + y;
function add(x) { return function(y) { return x + y; }; }
const request = defaults => options => { options =
Object.assign( {}, defaults, options ); return fetch(options.url, options) .then(resp => resp.json()); };
const request = defaults => options => { options =
Object.assign( {}, defaults, options ); return fetch(options.url, options) .then(resp => resp.json()); };
PIECING IT TOGETHER
const map = fn => array => array.map(fn); const multiply
= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
const map = fn => array => array.map(fn); const multiply
= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
const map = fn => array => array.map(fn); const multiply
= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
const map = fn => array => array.map(fn); const multiply
= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
const map = fn => array => array.map(fn); const multiply
= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
const map = fn => array => array.map(fn); const multiply
= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
const map = fn => array => array.map(fn); const multiply
= x => y => x * y; const pluck = key => object => object[key]; const discount = multiply(0.98); const tax = multiply(1.0925); const customRequest = request({ headers: { 'X-Custom': 'mykey' } }); customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax));
customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax)); [ { price:
5 }, { price: 10 }, { price: 3 } ]
[ { price: 5 }, { price: 10 }, {
price: 3 } ] map(pluck('price')) [ 5, 10, 3 ] item.price
[ 5, 10, 3 ] map(discount) [ 4.9, 9.8, 2.94
] price * 0.98
[ 5.35, 10.71, 3.21 ] map(tax) [ 4.9, 9.8, 2.94
] price * 1.0925
COMPOSING CLOSURES
None
const processWord = compose(hyphenate, reverse, toUpperCase); const words = [
'hello', 'functional', 'programming' ]; const newWords = words.map(processWord); console.log(newWords); // ['OL-LEH, 'LANOI-TCNUF', 'GNIMM-ARGORP']
const processWord = compose(hyphenate, reverse, toUpperCase); const words = [
'hello', 'functional', 'programming' ]; const newWords = words.map(processWord); console.log(newWords); // ['OL-LEH, 'LANOI-TCNUF', 'GNIMM-ARGORP']
const processWord = compose(hyphenate, reverse, toUpperCase); const processWordExplicit = (word)
=> { return hyphenate(reverse(toUpperCase(word))); };
customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax)); RETURNING TO PRICES
EXAMPLE
customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax)); Triple iteration RETURNING
TO PRICES EXAMPLE
customRequest({ url: '/cart/items' }) .then(map( compose( tax, discount, pluck('price') )
)); Single iteration
const compose = (...fns) => (...args) => { if (fns.length
=== 0) { return args[0]; } const last = fns[fns.length - 1]; const rest = fns.slice(0, -1); return rest.reduceRight((memo, fn) => { return fn(memo); }, last(...args)); };
const compose = (...fns) => (...args) => { if (fns.length
=== 0) { return args[0]; } const last = fns[fns.length - 1]; const rest = fns.slice(0, -1); return rest.reduceRight((memo, fn) => { return fn(memo); }, last(...args)); };
const compose = (...fns) => (...args) => { if (fns.length
=== 0) { return args[0]; } const last = fns[fns.length - 1]; const rest = fns.slice(0, -1); return rest.reduceRight((memo, fn) => { return fn(memo); }, last(...args)); };
const compose = (...fns) => (...args) => { if (fns.length
=== 0) { return args[0]; } const last = fns[fns.length - 1]; const rest = fns.slice(0, -1); return rest.reduceRight((memo, fn) => { return fn(memo); }, last(...args)); };
RECURSION SOLVE A PROBLEM IN TERMS OF ITSELF
FACTORIAL
const factorial = (n) => { let result = 1;
while (n > 1) { result *= n; n--; } return result; };
const factorial = (n) => { if (n < 2)
{ return 1; } return n * factorial(n - 1); };
const factorial = (n) => { if (n < 2)
{ return 1; } return n * factorial(n - 1); }; Recursive call
const factorial = (n) => { if (n < 2)
{ return 1; } return n * factorial(n - 1); }; Base case
factorial(4);
factorial(4); 4 * factorial(3);
factorial(4); 4 * factorial(3); 4 * 3 * factorial(2);
factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4
* 3 * 2 * factorial(1);
factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4
* 3 * 2 * factorial(1); 4 * 3 * 2 * 1;
factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4
* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2;
factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4
* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6;
factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4
* 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24;
STEPS Find the recurrence (n × n-1 × n-2 ×
… 1) Find the base case (n < 2)
PERFORMANCE RECURSION
const value = factorial(100000); console.log(value); // ??? WHAT IS THE
RESULT?
const value = factorial(100000); console.log(value); // ??? WHAT IS THE
RESULT? RangeError: Maximum call stack size exceeded
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> Call Stack
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) Call Stack
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) factorial(3) Call Stack
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) factorial(3) factorial(2) Call Stack
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) factorial(3) factorial(2) factorial(1) Call Stack
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) factorial(3) factorial(2) Call Stack
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) factorial(3) Call Stack
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) Call Stack
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> Call Stack
const value = factorial(100000); console.log(value); // ??? 100,000 calls =
100,000 stack frames 1 stack frame ≈ 48B Max stack usage ≈ 1MB 100,000 x 48 / 1024 / 1024 = 4.58MB > 1MB
IN ES2015! TAIL CALL OPTIMIZATION
const factorial = (n) => { if (n < 2)
{ return 1; } return n * factorial(n - 1); }; UNOPTIMIZABLE
const factorial = (n) => { if (n < 2)
{ return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE
const factorial = (n) => { if (n < 2)
{ return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE 2
const factorial = (n) => { if (n < 2)
{ return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE 2 3
const factorial = (n, accum = 1) => { if
(n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
const factorial = (n, accum = 1) => { if
(n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
const factorial = (n, accum = 1) => { if
(n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
const factorial = (n, accum = 1) => { if
(n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
const factorial = (n, accum = 1) => { if
(n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 OPTIMIZABLE
const factorial = (n, accum = 1) => { if
(n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2 OPTIMIZABLE
const factorial = (n, accum = 1) => { if
(n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2 3 OPTIMIZABLE
const value = factorial(100000); console.log(value); // Infinity
factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);
24; <main> Call Stack
factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);
24; <main> factorial(4, 1) Call Stack
factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);
24; <main> factorial(3, 4) Call Stack
factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);
24; <main> factorial(2, 12) Call Stack
factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);
24; <main> factorial(1, 24) Call Stack
factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);
24; <main> Call Stack
RECAP PREDICTABLE SAFE TRANSPARENT MODULAR
RESOURCES
drboolean.gitbooks.io/mostly-adequate-guide Brian Lonsdorf
ES6/7/LATER babeljs.io
LANGUAGES Elm (elm-lang.org) Clojurescript (github.com/clojure/clojurescript) Purescript (purescript.org)
LIBRARIES Lodash (lodash.com) Ramda (ramdajs.com) Rx (reactivex.io) Bacon.js (baconjs.github.io) Immutable.js
(facebook.github.io/immutable-js)
“MV*” React (facebook.github.io/react) Redux (redux.js.org)
THANKS! Jeremy Fairbank blog.jeremyfairbank.com @elpapapollo / jfairbank Code: github.com/jfairbank/fp-basics-in-es6