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.2k
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
200
CodeMash 2020: Solving the Boolean Identity Crisis
jfairbank
1
150
CodeMash 2020: Practical Functional Programming
jfairbank
1
320
Connect.Tech 2019: Practical Functional Programming
jfairbank
0
350
Connect.Tech 2019: Solving the Boolean Identity Crisis
jfairbank
0
180
Lambda Squared 2019: Solving the Boolean Identity Crisis
jfairbank
0
120
All Things Open 2018: Practical Functional Programming
jfairbank
2
260
Connect.Tech 2018: Effective React Testing
jfairbank
1
160
Fluent Conf 2018: Building web apps with Elm Tutorial
jfairbank
2
850
Other Decks in Programming
See All in Programming
階層化自動テストで開発に機動力を
ickx
1
410
レベル1の開発生産性向上に取り組む − 日々の作業の効率化・自動化を通じた改善活動
kesoji
1
350
なぜあなたのオブザーバビリティ導入は頓挫するのか
ryota_hnk
0
360
リバースエンジニアリング新時代へ! GhidraとClaude DesktopをMCPで繋ぐ/findy202507
tkmru
4
1.2k
ご注文の差分はこちらですか? 〜 AWS CDK のいろいろな差分検出と安全なデプロイ
konokenj
4
670
Claude Code で Astro blog を Pages から Workers へ移行してみた
codehex
0
150
効率的な開発手段として VRTを活用する
ishkawa
1
180
Googleの新しいコーディングAIエージェントJulesを使ってみた
tonionagauzzi
0
120
The Modern View Layer Rails Deserves: A Vision For 2025 And Beyond @ RailsConf 2025, Philadelphia, PA
marcoroth
2
780
コーディングエージェント概観(2025/07)
itsuki_t88
0
130
PHPUnitの限界をPlaywrightで補完するテストアプローチ
yuzneri
0
270
状態遷移図を書こう / Sequence Chart vs State Diagram
orgachem
PRO
3
250
Featured
See All Featured
Building a Scalable Design System with Sketch
lauravandoore
462
33k
Raft: Consensus for Rubyists
vanstee
140
7k
Bootstrapping a Software Product
garrettdimon
PRO
307
110k
Visualization
eitanlees
146
16k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.4k
Side Projects
sachag
455
43k
Become a Pro
speakerdeck
PRO
29
5.4k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.8k
Typedesign – Prime Four
hannesfritz
42
2.7k
BBQ
matthewcrist
89
9.7k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
How to train your dragon (web standard)
notwaldorf
96
6.1k
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