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
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
160
CodeMash 2020: Solving the Boolean Identity Crisis
jfairbank
1
120
CodeMash 2020: Practical Functional Programming
jfairbank
1
290
Connect.Tech 2019: Practical Functional Programming
jfairbank
0
300
Connect.Tech 2019: Solving the Boolean Identity Crisis
jfairbank
0
150
Lambda Squared 2019: Solving the Boolean Identity Crisis
jfairbank
0
75
All Things Open 2018: Practical Functional Programming
jfairbank
2
240
Connect.Tech 2018: Effective React Testing
jfairbank
1
130
Fluent Conf 2018: Building web apps with Elm Tutorial
jfairbank
2
760
Other Decks in Programming
See All in Programming
初心者のためのRubyKaigi入門/RubyKaigi Introduction
a_matsuda
2
850
GitHub Actionsで泣かないためにやっておきたい設定 / Recommended GHA settings to avoid crying
pinkumohikan
3
540
Goのエラースタックトレースの歴史と今後
sonatard
9
1.5k
TCAとKMPを用いた新規動画配信アプリ 「ABEMA Live」の設計
tomu28
1
110
try! Swift Tokyo 初参加報告LT
hinakko2
0
220
VS Code をプロダクトにどう取り込むか
onomax
1
370
Git Lint
bkuhlmann
4
750
DMMプラットフォームがTiDB Cloudを採用した背景
pospome
8
4.1k
効率化に挑戦してみたらモバイル開発が少し快適になった話
ryunakayama
0
130
Ruby GitHub Packages
bkuhlmann
0
630
Hanami and htmx
bkuhlmann
0
210
Code Reviews
bkuhlmann
4
890
Featured
See All Featured
Practical Orchestrator
shlominoach
182
9.7k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
501
140k
Java REST API Framework Comparison - PWX 2021
mraible
PRO
18
6.9k
Put a Button on it: Removing Barriers to Going Fast.
kastner
58
3.1k
How GitHub Uses GitHub to Build GitHub
holman
468
290k
Visualization
eitanlees
136
14k
Designing for humans not robots
tammielis
248
25k
Making the Leap to Tech Lead
cromwellryan
124
8.5k
Into the Great Unknown - MozCon
thekraken
10
1k
Music & Morning Musume
bryan
41
5.6k
In The Pink: A Labor of Love
frogandcode
138
21k
Being A Developer After 40
akosma
57
580k
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