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
CodeMash: Functional Programming Basics in ES6
Search
Jeremy Fairbank
January 07, 2016
Programming
4
1.1k
CodeMash: Functional Programming Basics in ES6
Jeremy Fairbank
January 07, 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
97
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
Amazon Neptuneで始めてみるグラフDB-OpenSearchによるグラフの全文検索-
satoshi256kbyte
4
350
Webの技術スタックで マルチプラットフォームアプリ開発を可能にするElixirDesktopの紹介
thehaigo
2
1k
Go言語でターミナルフレンドリーなAIコマンド、afaを作った/fukuokago20_afa
monochromegane
2
140
Sidekiqで実現する 長時間非同期処理の中断と再開 / Pausing and Resuming Long-Running Asynchronous Jobs with Sidekiq
hypermkt
6
2.8k
WEBエンジニア向けAI活用入門
sutetotanuki
0
310
デプロイを任されたので、教わった通りにデプロイしたら障害になった件 ~俺のやらかしを越えてゆけ~
techouse
52
33k
NSOutlineView何もわからん:( 前編 / I Don't Understand About NSOutlineView :( Pt. 1
usagimaru
0
230
Why Spring Matters to Jakarta EE - and Vice Versa
ivargrimstad
0
1.2k
Generative AI Use Cases JP (略称:GenU)奮闘記
hideg
0
180
Realtime API 入門
riofujimon
0
120
開発効率向上のためのリファクタリングの一歩目の選択肢 ~コード分割~ / JJUG CCC 2024 Fall
ryounasso
0
390
詳細解説! ArrayListの仕組みと実装
yujisoftware
0
500
Featured
See All Featured
Making the Leap to Tech Lead
cromwellryan
133
8.9k
Statistics for Hackers
jakevdp
796
220k
The Cult of Friendly URLs
andyhume
78
6k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
27
4.2k
Bootstrapping a Software Product
garrettdimon
PRO
305
110k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
250
21k
Why You Should Never Use an ORM
jnunemaker
PRO
53
9k
How GitHub (no longer) Works
holman
310
140k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
228
52k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
505
140k
Building an army of robots
kneath
302
42k
Transcript
FUNCTIONAL PROGRAMMING Jeremy Fairbank blog.jeremyfairbank.com @elpapapollo / jfairbank BASICS IN
ES6
We help brands excel. pushagency.io Your website, SimplyBuilt. simplybuilt.com
Redux
¯\_(ϑ)_/¯ 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', 'codemash'); // hello codemash
const array = (...elements) => { return elements; }; array(1,
2, 3); // [1, 2, 3] const log = (...args) => { console.log(...args); }; log('hello', 'codemash'); // hello codemash
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('Codemash', 'Hello'); // Hello Codemash greet('Sandusky'); // Hi Sandusky
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'); n.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 => { return request(Object.assign(
{}, defaults, options )); };
const request = defaults => options => { return request(Object.assign(
{}, defaults, options )); };
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); }; OPTIMIZED
const factorial = (n, accum = 1) => { if
(n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZED
const factorial = (n, accum = 1) => { if
(n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZED
const factorial = (n, accum = 1) => { if
(n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZED
OPTIMIZED const factorial = (n, accum = 1) => {
if (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1
OPTIMIZED const factorial = (n, accum = 1) => {
if (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2
OPTIMIZED const factorial = (n, accum = 1) => {
if (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2 3
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 Slides:
bit.ly/1OR20R7