CodeMash: Functional Programming Basics in ES6

CodeMash: Functional Programming Basics in ES6

94bd558238b69c45d3d3e15797ae94f7?s=128

Jeremy Fairbank

January 07, 2016
Tweet

Transcript

  1. 3.
  2. 6.
  3. 8.
  4. 9.
  5. 11.
  6. 18.

    All good let age = 28; age = 29; const

    name = 'Jeremy'; name = 'Jet'; 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 array = (...elements) => { return elements; }; array(1,

    2, 3); // [1, 2, 3] const log = (...args) => { console.log(...args); }; log('hello', 'codemash'); // hello codemash
  10. 22.

    const array = (...elements) => { return elements; }; array(1,

    2, 3); // [1, 2, 3] const log = (...args) => { console.log(...args); }; log('hello', 'codemash'); // hello codemash
  11. 23.

    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;
  12. 24.

    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;
  13. 25.

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

    name); }; greet('Codemash', 'Hello'); // Hello Codemash greet('Sandusky'); // Hi Sandusky
  14. 26.

    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; };
  15. 27.
  16. 28.

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

    3) === 5; add(2, 3) === 5; add(2, 3) === 5;
  17. 29.

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

    3) === 5; add(2, 3) === 5; add(2, 3) === 5; Referentially transparent
  18. 30.

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

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

    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'); }); }); ×
  20. 33.

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

    it('returns an uppercase name', () => { expect(upperName('Jeremy')).to.equal('JEREMY'); expect(upperName('Jet')).to.equal('JET'); }); }); ✓
  21. 35.

    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]
  22. 37.
  23. 40.

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

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

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

    = hobbies.splice(0, 2); console.log(firstTwo); // ['programming', 'reading'] console.log(hobbies); // ['music'] Slight typo + mutability
  25. 45.

    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] ×
  26. 46.

    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)
  27. 47.

    PROS SAFETY FREE UNDO/REDO LOGS — REDUX EXPLICIT FLOW OF

    DATA LESS MEMORY USAGE* CONCURRENCY SAFETY* *In certain cases
  28. 48.

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

    USAGE* *Alleviated with libs like Immutable.js
  29. 50.

    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);
  30. 51.

    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);
  31. 52.

    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);
  32. 53.

    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);
  33. 55.

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

    + y; }; const add3 = createAdder(3); add3(2) === 5; add3(3) === 6;
  34. 56.

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

    + y; }; const add3 = createAdder(3); add3(2) === 5; add3(3) === 6;
  35. 57.

    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' } });
  36. 58.

    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
  37. 59.

    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' });
  38. 60.

    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' });
  39. 63.

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

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

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

    add3 = partial(add, 3); add3(2) === 5;
  41. 65.

    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' });
  42. 66.

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

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  43. 67.

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

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  44. 68.

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

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  45. 69.

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

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  46. 70.

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

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  47. 71.

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

    }; const partial = (fn, ...args) => { return (...otherArgs) => { return fn(...args, ...otherArgs) }; };
  48. 72.
  49. 73.

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

    request({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
  50. 74.

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

    request({ headers: { 'X-Custom': 'mykey' } }); const usersPromise = customRequest({ url: '/users' }); const tasksPromise = customRequest({ url: '/tasks' });
  51. 75.

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

    function add(x) { return function(y) { return x + y; }; }
  52. 76.

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

    function add(x) { return function(y) { return x + y; }; }
  53. 77.

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

    function add(x) { return function(y) { return x + y; }; }
  54. 81.

    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));
  55. 82.

    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));
  56. 83.

    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));
  57. 84.

    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));
  58. 85.

    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));
  59. 86.

    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));
  60. 87.

    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));
  61. 89.

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

    price: 3 } ] map(pluck('price')) [ 5, 10, 3 ] item.price
  62. 93.
  63. 94.

    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']
  64. 95.

    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']
  65. 100.

    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)); };
  66. 101.

    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)); };
  67. 102.

    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)); };
  68. 103.

    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)); };
  69. 105.
  70. 106.

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

    while (n > 1) { result *= n; n--; } return result; };
  71. 107.

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

    { return 1; } return n * factorial(n - 1); };
  72. 108.

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

    { return 1; } return n * factorial(n - 1); }; Recursive call
  73. 109.

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

    { return 1; } return n * factorial(n - 1); }; Base case
  74. 114.

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

    * 3 * 2 * factorial(1); 4 * 3 * 2 * 1;
  75. 115.

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

    * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2;
  76. 116.

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

    * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6;
  77. 117.

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

    * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24;
  78. 118.

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

    … 1)
 Find the base case
 (n < 2)
  79. 121.

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

    RESULT? RangeError: Maximum call stack size exceeded
  80. 122.

    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
  81. 123.

    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
  82. 124.

    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
  83. 125.

    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
  84. 126.

    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
  85. 127.

    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
  86. 128.

    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
  87. 129.

    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
  88. 130.

    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
  89. 131.

    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
  90. 133.

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

    { return 1; } return n * factorial(n - 1); }; UNOPTIMIZABLE
  91. 134.

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

    { return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE
  92. 135.

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

    { return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE 2
  93. 136.

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

    { return 1; } return n * factorial(n - 1); }; 1 UNOPTIMIZABLE 2 3
  94. 137.

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

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZED
  95. 138.

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

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZED
  96. 139.

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

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZED
  97. 140.

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

    (n < 2) { return accum; } return factorial(n - 1, n * accum); }; OPTIMIZED
  98. 141.

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

    if (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1
  99. 142.

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

    if (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2
  100. 143.

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

    if (n < 2) { return accum; } return factorial(n - 1, n * accum); }; 1 2 3
  101. 152.