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. 3.
  2. 6.
  3. 8.
  4. 9.
  5. 11.
  6. 18.

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

    'Tucker'; name = 'Sally'; All good Syntax error
  7. 19.

    const add = (x, y) => { return x +

    y; }; const identity = x => x;
  8. 20.

    const add = (x, y) => { return x +

    y; }; const identity = x => x;
  9. 21.

    const add = (x, y) => { return x +

    y; }; const identity = x => x;
  10. 22.

    const add = (x, y) => { return x +

    y; }; const identity = x => x;
  11. 23.

    const add = (x, y) => { return x +

    y; }; const identity = x => x;
  12. 24.

    const add = (x, y) => { return x +

    y; }; const identity = x => x;
  13. 31.

    const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

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

    const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

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

    const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

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

    const langs = [ 'JavaScript', 'Elm', 'Haskell', ]; const [js,

    ...rest] = langs; js === 'JavaScript'; rest[0] === 'Elm'; rest[1] === 'Haskell';
  17. 38.

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

    name); }; greet('DevNexus', 'Hello'); // Hello DevNexus greet('Atlanta'); // Hi Atlanta
  18. 39.

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

    name); }; greet('DevNexus', 'Hello'); // Hello DevNexus greet('Atlanta'); // Hi Atlanta
  19. 40.

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

    name); }; greet('DevNexus', 'Hello'); // Hello DevNexus greet('Atlanta'); // Hi Atlanta
  20. 41.

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

    name); }; greet('DevNexus', 'Hello'); // Hello DevNexus greet('Atlanta'); // Hi Atlanta
  21. 42.

    Object.assign( {}, { hello: 'Atlanta' }, { hi: 'DevNexus' }

    ); // { // hello: 'Atlanta', // hi: 'DevNexus' // }
  22. 43.

    Object.assign( {}, { hello: 'Atlanta' }, { hi: 'DevNexus' }

    ); // { // hello: 'Atlanta', // hi: 'DevNexus' // }
  23. 44.

    Object.assign( {}, { hello: 'Atlanta' }, { hi: 'DevNexus' }

    ); // { // hello: 'Atlanta', // hi: 'DevNexus' // }
  24. 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; };
  25. 46.
  26. 47.

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

    3) === 5; add(2, 3) === 5; add(2, 3) === 5;
  27. 48.

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

    3) === 5; add(2, 3) === 5; add(2, 3) === 5; Referentially transparent
  28. 49.

    × let name = 'Jeremy'; const getName = () =>

    name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
  29. 50.

    × let name = 'Jeremy'; const getName = () =>

    name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
  30. 51.

    × let name = 'Jeremy'; const getName = () =>

    name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
  31. 52.

    × let name = 'Jeremy'; const getName = () =>

    name; const setName = (newName) => { name = newName; }; const printUpperName = () => { console.log(name.toUpperCase()); };
  32. 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'); }); }); ×
  33. 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'); }); }); ✓
  34. 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]
  35. 59.
  36. 60.
  37. 63.

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

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

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

    = hobbies.splice(0, 2); console.log(firstTwo); // ['programming', 'reading'] console.log(hobbies); // ['music'] Slight typo + mutability
  39. 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] ×
  40. 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)
  41. 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)
  42. 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)
  43. 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)
  44. 74.
  45. 75.

    CONS VERBOSE MORE OBJECT CREATION* MORE GARBAGE COLLECTION* MORE MEMORY

    USAGE* *Alleviated with libs like Immutable.js
  46. 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);
  47. 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);
  48. 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);
  49. 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);
  50. 82.

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

    + y; }; const add3 = createAdder(3); add3(2) === 5; add3(3) === 6;
  51. 83.

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

    + y; }; const add3 = createAdder(3); add3(2) === 5; add3(3) === 6;
  52. 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' } });
  53. 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
  54. 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' });
  55. 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' });
  56. 90.

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

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

    const add = (x, y) => x + y; const

    add3 = partial(add, 3); add3(2) === 5;
  58. 92.

    const add = (x, y) => x + y; const

    add3 = partial(add, 3); add3(2) === 5;
  59. 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' });
  60. 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' });
  61. 95.

    const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

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

    const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

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

    const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

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

    const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

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

    const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

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

    const partialFromBind = (fn, ...args) => { return fn.bind(null, ...args);

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  67. 101.
  68. 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' });
  69. 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' });
  70. 104.

    const add = x => y => x + y;

    function add(x) { return function(y) { return x + y; }; }
  71. 105.

    const add = x => y => x + y;

    function add(x) { return function(y) { return x + y; }; }
  72. 106.

    const add = x => y => x + y;

    function add(x) { return function(y) { return x + y; }; }
  73. 107.

    const request = defaults => options => { options =

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

    const request = defaults => options => { options =

    Object.assign( {}, defaults, options ); return fetch(options.url, options) .then(resp => resp.json()); };
  75. 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));
  76. 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));
  77. 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));
  78. 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));
  79. 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));
  80. 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));
  81. 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));
  82. 118.

    [ { price: 5 }, { price: 10 }, {

    price: 3 }, ] map(pluck('price')) [ 5, 10, 3, ] item.price
  83. 122.
  84. 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']
  85. 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']
  86. 132.

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

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

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

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

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

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

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

    fn) => fn(result), arg ) ); fns = [] result = 'OL-LEH'
  90. 140.
  91. 141.

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

    while (n > 1) { result *= n; n--; } return result; };
  92. 142.

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

    { return 1; } return n * factorial(n - 1); };
  93. 143.

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

    { return 1; } return n * factorial(n - 1); }; Recursive call
  94. 144.

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

    { return 1; } return n * factorial(n - 1); }; Base case
  95. 149.

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

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

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

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

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

    * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6;
  98. 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;
  99. 153.

    STEPS Find the recurrence
 (n × n-1 × n-2 ×

    … 1)
 Find the base case
 (n < 2)
  100. 156.

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

    RESULT? RangeError: Maximum call stack size exceeded
  101. 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
  102. 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
  103. 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
  104. 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
  105. 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
  106. 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
  107. 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
  108. 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
  109. 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
  110. 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
  111. 168.

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

    { return 1; } return n * factorial(n - 1); }; UNOPTIMIZABLE
  112. 169.

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

    { return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE
  113. 170.

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

    { return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE 2
  114. 171.

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

    { return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE 2 3
  115. 172.

    const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
  116. 173.

    const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
  117. 174.

    const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
  118. 175.

    const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZABLE
  119. 176.

    const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 OPTIMIZABLE
  120. 177.

    const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2 OPTIMIZABLE
  121. 178.

    const factorial = (n, accum = 1) => { if

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2 3 OPTIMIZABLE
  122. 187.
  123. 188.