DevNexus 2017: Functional Programming Basics in ES6

DevNexus 2017: Functional Programming Basics in ES6

94bd558238b69c45d3d3e15797ae94f7?s=128

Jeremy Fairbank

February 23, 2017
Tweet

Transcript

  1. FUNCTIONAL PROGRAMMING Jeremy Fairbank blog.jeremyfairbank.com @elpapapollo / jfairbank BASICS IN

    ES6
  2. sigient.com

  3. None
  4. ¯\_(ϑ)_/¯ WHAT IS FUNCTIONAL PROGRAMMING?

  5. ¯\_(ϑ)_/¯ WHY FUNCTIONAL PROGRAMMING?

  6. None
  7. Domain to Range

  8. None
  9. None
  10. Domain Range

  11. CALCULUS

  12. PRINCIPLES

  13. PURE & DECLARATIVE PREDICTABLE

  14. IMMUTABLE STATE SAFE

  15. FIRST CLASS STATE TRANSPARENT

  16. COMPOSABLE FIRST CLASS CLOSURES MODULAR

  17. ES2015 (ES6)

  18. let age = 10; age = 11; const name =

    'Tucker'; name = 'Sally'; All good Syntax error
  19. const add = (x, y) => { return x +

    y; }; const identity = x => x;
  20. const add = (x, y) => { return x +

    y; }; const identity = x => x;
  21. const add = (x, y) => { return x +

    y; }; const identity = x => x;
  22. const add = (x, y) => { return x +

    y; }; const identity = x => x;
  23. const add = (x, y) => { return x +

    y; }; const identity = x => x;
  24. const add = (x, y) => { return x +

    y; }; const identity = x => x;
  25. const array = (...elements) => { return elements; }; array(1,

    2, 3); // [1, 2, 3]
  26. const array = (...elements) => { return elements; }; array(1,

    2, 3); // [1, 2, 3]
  27. const array = (...elements) => { return elements; }; array(1,

    2, 3); // [1, 2, 3]
  28. const array = (...elements) => { return elements; }; array(1,

    2, 3); // [1, 2, 3]
  29. const log = (...args) => { console.log(...args); }; log('Hello', 'DevNexus');

    // Hello DevNexus
  30. const log = (...args) => { console.log(...args); }; log('Hello', 'DevNexus');

    // Hello DevNexus
  31. const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

    ...rest] = langs; js === 'JavaScript'; rest[0] === 'Elm'; rest[1] === 'Haskell';
  32. const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

    ...rest] = langs; js === 'JavaScript'; rest[0] === 'Elm'; rest[1] === 'Haskell';
  33. const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

    ...rest] = langs; js === 'JavaScript'; rest[0] === 'Elm'; rest[1] === 'Haskell';
  34. const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

    ...rest] = langs; js === 'JavaScript'; rest[0] === 'Elm'; rest[1] === 'Haskell';
  35. const head = ([x]) => x; head([1, 2, 3]) ===

    1;
  36. const head = ([x]) => x; head([1, 2, 3]) ===

    1;
  37. const head = ([x]) => x; head([1, 2, 3]) ===

    1;
  38. const greet = (name, greeting = 'Hi') => { console.log(greeting,

    name); }; greet('DevNexus', 'Hello'); // Hello DevNexus greet('Atlanta'); // Hi Atlanta
  39. const greet = (name, greeting = 'Hi') => { console.log(greeting,

    name); }; greet('DevNexus', 'Hello'); // Hello DevNexus greet('Atlanta'); // Hi Atlanta
  40. const greet = (name, greeting = 'Hi') => { console.log(greeting,

    name); }; greet('DevNexus', 'Hello'); // Hello DevNexus greet('Atlanta'); // Hi Atlanta
  41. const greet = (name, greeting = 'Hi') => { console.log(greeting,

    name); }; greet('DevNexus', 'Hello'); // Hello DevNexus greet('Atlanta'); // Hi Atlanta
  42. Object.assign( {}, { hello: 'Atlanta' }, { hi: 'DevNexus' }

    ); // { // hello: 'Atlanta', // hi: 'DevNexus' // }
  43. Object.assign( {}, { hello: 'Atlanta' }, { hi: 'DevNexus' }

    ); // { // hello: 'Atlanta', // hi: 'DevNexus' // }
  44. Object.assign( {}, { hello: 'Atlanta' }, { hi: 'DevNexus' }

    ); // { // hello: 'Atlanta', // hi: 'DevNexus' // }
  45. 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; };
  46. PURE

  47. const add = (x, y) => x + y; add(2,

    3) === 5; add(2, 3) === 5; add(2, 3) === 5;
  48. const add = (x, y) => x + y; add(2,

    3) === 5; add(2, 3) === 5; add(2, 3) === 5; Referentially transparent
  49. × let name = 'Jeremy'; const getName = () =>

    name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
  50. × let name = 'Jeremy'; const getName = () =>

    name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
  51. × let name = 'Jeremy'; const getName = () =>

    name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
  52. × let name = 'Jeremy'; const getName = () =>

    name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
  53. 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'); }); }); ×
  54. HIDDEN STATE IS UNCERTAIN STATE

  55. const upperName = (name) => name.toUpperCase(); describe('api', () => {

    it('returns an uppercase name', () => { expect(upperName('Jeremy')).to.equal('JEREMY'); expect(upperName('Jet')).to.equal('JET'); }); }); ✓
  56. HOW TO ACHIEVE THE RESULT IMPERATIVE

  57. 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]
  58. DECLARE WHAT THE DESIRED RESULT IS DECLARATIVE

  59. function doubleNumbers(numbers) { return numbers.map(n => n * 2); }

    doubleNumbers([1, 2, 3]); // [2, 4, 6]
  60. function doubleNumbers(numbers) { return numbers.map(n => n * 2); }

    doubleNumbers([1, 2, 3]); // [2, 4, 6]
  61. 1 2 3 numbers.map(n => n * 2) 2 4

    6 Domain Range
  62. CREATE STATE, DON’T MUTATE IT IMMUTABLE

  63. const hobbies = [ 'programming', 'reading', 'music', ]; const firstTwo

    = hobbies.splice(0, 2); console.log(firstTwo); // ['programming', 'reading'] console.log(hobbies); // ['music']
  64. const hobbies = [ 'programming', 'reading', 'music', ]; const firstTwo

    = hobbies.splice(0, 2); console.log(firstTwo); // ['programming', 'reading'] console.log(hobbies); // ['music'] Slight typo + mutability
  65. Object.freeze Immutable.js

  66. const hobbies = Object.freeze([ 'programming', 'reading', 'music', ]); const firstTwo

    = hobbies.splice(0, 2); // TypeError
  67. const hobbies = Object.freeze([ 'programming', 'reading', 'music', ]); const firstTwo

    = hobbies.splice(0, 2); // TypeError
  68. FREE YOUR STATE

  69. 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] ×
  70. 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)
  71. 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)
  72. 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)
  73. 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)
  74. PROS SAFETY FROM ACCIDENTAL MUTATION FREE UNDO/REDO LOGS — REDUX

    EXPLICIT FLOW OF DATA CONCURRENCY SAFETY
  75. CONS VERBOSE MORE OBJECT CREATION* MORE GARBAGE COLLECTION* MORE MEMORY

    USAGE* *Alleviated with libs like Immutable.js
  76. FIRST CLASS FUNCTIONS

  77. 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);
  78. 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);
  79. 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);
  80. 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);
  81. ENCAPSULATION CLOSURES

  82. const createAdder = (x) => { return (y) => x

    + y; }; const add3 = createAdder(3); add3(2) === 5; add3(3) === 6;
  83. const createAdder = (x) => { return (y) => x

    + y; }; const add3 = createAdder(3); add3(2) === 5; add3(3) === 6;
  84. 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' } });
  85. 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
  86. 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' });
  87. 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' });
  88. FOUNDATION FOR HIGHER ORDER PATTERNS FIRST CLASS CLOSURES

  89. PARTIAL APPLICATION

  90. const createAdder = (x) => { return (y) => x

    + y; }; const createRequester = (options) => { return (otherOptions) => { return request(Object.assign( {}, options, otherOptions )); }; }; RECALL
  91. const add = (x, y) => x + y; const

    add3 = partial(add, 3); add3(2) === 5;
  92. const add = (x, y) => x + y; const

    add3 = partial(add, 3); add3(2) === 5;
  93. 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' });
  94. 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' });
  95. const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  96. const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  97. const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  98. const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  99. const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  100. const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  101. CURRYING

  102. const add3 = add(3); add3(2) === 5; const customRequest =

    request({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
  103. const add3 = add(3); add3(2) === 5; const customRequest =

    request({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
  104. const add = x => y => x + y;

    function add(x) { return function(y) { return x + y; }; }
  105. const add = x => y => x + y;

    function add(x) { return function(y) { return x + y; }; }
  106. const add = x => y => x + y;

    function add(x) { return function(y) { return x + y; }; }
  107. const request = defaults => options => { options =

    Object.assign( {}, defaults, options ); return fetch(options.url, options) .then(resp => resp.json()); };
  108. const request = defaults => options => { options =

    Object.assign( {}, defaults, options ); return fetch(options.url, options) .then(resp => resp.json()); };
  109. PIECING IT TOGETHER

  110. 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));
  111. 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));
  112. 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));
  113. 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));
  114. 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));
  115. 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));
  116. 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));
  117. customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax)); [ { price:

    5 }, { price: 10 }, { price: 3 }, ]
  118. [ { price: 5 }, { price: 10 }, {

    price: 3 }, ] map(pluck('price')) [ 5, 10, 3, ] item.price
  119. [ 5, 10, 3, ] map(discount) [ 4.9, 9.8, 2.94,

    ] price * 0.98
  120. [ 5.35, 10.71, 3.21, ] map(tax) [ 4.9, 9.8, 2.94,

    ] price * 1.0925
  121. COMPOSING CLOSURES

  122. None
  123. 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']
  124. 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']
  125. const processWord = compose(hyphenate, reverse, toUpperCase); const processWordExplicit = (word)

    => { return hyphenate(reverse(toUpperCase(word))); };
  126. const compose = (...fns) => arg => ( fns.reduceRight( (result,

    fn) => fn(result), arg ) );
  127. const compose = (...fns) => arg => ( fns.reduceRight( (result,

    fn) => fn(result), arg ) );
  128. const compose = (...fns) => arg => ( fns.reduceRight( (result,

    fn) => fn(result), arg ) );
  129. const compose = (...fns) => arg => ( fns.reduceRight( (result,

    fn) => fn(result), arg ) );
  130. const compose = (...fns) => arg => ( fns.reduceRight( (result,

    fn) => fn(result), arg ) );
  131. const compose = (...fns) => arg => ( fns.reduceRight( (result,

    fn) => fn(result), arg ) );
  132. const compose = (...fns) => arg => ( fns.reduceRight( (result,

    fn) => fn(result), arg ) ); fns = [hyphenate, reverse, toUpperCase] arg = result = 'hello'
  133. const compose = (...fns) => arg => ( fns.reduceRight( (result,

    fn) => fn(result), arg ) ); fns = [hyphenate, reverse] result = 'HELLO'
  134. const compose = (...fns) => arg => ( fns.reduceRight( (result,

    fn) => fn(result), arg ) ); fns = [hyphenate] result = 'OLLEH'
  135. const compose = (...fns) => arg => ( fns.reduceRight( (result,

    fn) => fn(result), arg ) ); fns = [] result = 'OL-LEH'
  136. customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax)); RETURNING TO PRICES

    EXAMPLE
  137. customRequest({ url: '/cart/items' }) .then(map(pluck('price'))) .then(map(discount)) .then(map(tax)); Triple iteration RETURNING

    TO PRICES EXAMPLE
  138. customRequest({ url: '/cart/items' }) .then(map( compose( tax, discount, pluck('price') )

    )); Single iteration
  139. RECURSION SOLVE A PROBLEM IN TERMS OF ITSELF

  140. FACTORIAL

  141. const factorial = (n) => { let result = 1;

    while (n > 1) { result *= n; n--; } return result; };
  142. const factorial = (n) => { if (n < 2)

    { return 1; } return n * factorial(n - 1); };
  143. const factorial = (n) => { if (n < 2)

    { return 1; } return n * factorial(n - 1); }; Recursive call
  144. const factorial = (n) => { if (n < 2)

    { return 1; } return n * factorial(n - 1); }; Base case
  145. factorial(4);

  146. factorial(4); 4 * factorial(3);

  147. factorial(4); 4 * factorial(3); 4 * 3 * factorial(2);

  148. factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

    * 3 * 2 * factorial(1);
  149. factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

    * 3 * 2 * factorial(1); 4 * 3 * 2 * 1;
  150. factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

    * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2;
  151. factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

    * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6;
  152. factorial(4); 4 * factorial(3); 4 * 3 * factorial(2); 4

    * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24;
  153. STEPS Find the recurrence
 (n × n-1 × n-2 ×

    … 1)
 Find the base case
 (n < 2)
  154. PERFORMANCE RECURSION

  155. const value = factorial(100000); console.log(value); // ??? WHAT IS THE

    RESULT?
  156. const value = factorial(100000); console.log(value); // ??? WHAT IS THE

    RESULT? RangeError: Maximum call stack size exceeded
  157. 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
  158. 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
  159. 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
  160. 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
  161. 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
  162. 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
  163. 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
  164. 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
  165. 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
  166. 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
  167. IN ES2015! TAIL CALL OPTIMIZATION

  168. const factorial = (n) => { if (n < 2)

    { return 1; } return n * factorial(n - 1); }; UNOPTIMIZABLE
  169. const factorial = (n) => { if (n < 2)

    { return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE
  170. const factorial = (n) => { if (n < 2)

    { return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE 2
  171. const factorial = (n) => { if (n < 2)

    { return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE 2 3
  172. const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
  173. const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
  174. const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
  175. const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
  176. const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 OPTIMIZABLE
  177. const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2 OPTIMIZABLE
  178. const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2 3 OPTIMIZABLE
  179. const value = factorial(100000); console.log(value); // Infinity

  180. factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

    24; <main> Call Stack
  181. factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

    24; <main> factorial(4, 1) Call Stack
  182. factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

    24; <main> factorial(3, 4) Call Stack
  183. factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

    24; <main> factorial(2, 12) Call Stack
  184. factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

    24; <main> factorial(1, 24) Call Stack
  185. factorial(4 /*, 1 */); factorial(3, 4); factorial(2, 12); factorial(1, 24);

    24; <main> Call Stack
  186. RECAP PREDICTABLE SAFE TRANSPARENT MODULAR

  187. DEMO

  188. RESOURCES

  189. drboolean.gitbooks.io/mostly-adequate-guide Brian Lonsdorf

  190. ES6/7/LATER babeljs.io

  191. LANGUAGES Elm (elm-lang.org) Clojurescript (github.com/clojure/clojurescript) Purescript (purescript.org)

  192. LIBRARIES Lodash (lodash.com) Ramda (ramdajs.com) Rx (reactivex.io) Bacon.js (baconjs.github.io) Immutable.js

    (facebook.github.io/immutable-js)
  193. “MV*” React (facebook.github.io/react) Redux (redux.js.org)

  194. THANKS! Jeremy Fairbank blog.jeremyfairbank.com @elpapapollo / jfairbank Code: github.com/jfairbank/fp-basics-in-es6 Slides:

    bit.ly/devnexus-fp-basics