Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DevNexus 2017: Functional Programming Basics in ES6

DevNexus 2017: Functional Programming Basics in ES6

Jeremy Fairbank

February 23, 2017
Tweet

More Decks by Jeremy Fairbank

Other Decks in Programming

Transcript

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

    View full-size slide

  2. ¯\_(ϑ)_/¯
    WHAT IS FUNCTIONAL
    PROGRAMMING?

    View full-size slide

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

    View full-size slide

  4. Domain to Range

    View full-size slide

  5. Domain Range

    View full-size slide

  6. PURE & DECLARATIVE
    PREDICTABLE

    View full-size slide

  7. IMMUTABLE STATE
    SAFE

    View full-size slide

  8. FIRST CLASS STATE
    TRANSPARENT

    View full-size slide

  9. COMPOSABLE FIRST
    CLASS CLOSURES
    MODULAR

    View full-size slide

  10. let age = 10;
    age = 11;
    const name = 'Tucker';
    name = 'Sally';
    All good
    Syntax error

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  37. 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;
    };

    View full-size slide

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

    View full-size slide

  39. const add = (x, y) => x + y;
    add(2, 3) === 5;
    add(2, 3) === 5;
    add(2, 3) === 5;
    Referentially
    transparent

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  45. HIDDEN STATE IS
    UNCERTAIN STATE

    View full-size slide

  46. const upperName = (name) => name.toUpperCase();
    describe('api', () => {
    it('returns an uppercase name', () => {
    expect(upperName('Jeremy')).to.equal('JEREMY');
    expect(upperName('Jet')).to.equal('JET');
    });
    });

    View full-size slide

  47. HOW TO ACHIEVE THE
    RESULT
    IMPERATIVE

    View full-size slide

  48. 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]

    View full-size slide

  49. DECLARE WHAT THE
    DESIRED RESULT IS
    DECLARATIVE

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  52. 1
    2
    3
    numbers.map(n => n * 2)
    2
    4
    6
    Domain Range

    View full-size slide

  53. CREATE STATE, DON’T
    MUTATE IT
    IMMUTABLE

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  56. Object.freeze
    Immutable.js

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  59. FREE YOUR
    STATE

    View full-size slide

  60. 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]
    ×

    View full-size slide

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

    View full-size slide

  62. 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)

    View full-size slide

  63. 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)

    View full-size slide

  64. 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)

    View full-size slide

  65. PROS
    SAFETY FROM ACCIDENTAL MUTATION
    FREE UNDO/REDO LOGS — REDUX
    EXPLICIT FLOW OF DATA
    CONCURRENCY SAFETY

    View full-size slide

  66. CONS
    VERBOSE
    MORE OBJECT CREATION*
    MORE GARBAGE COLLECTION*
    MORE MEMORY USAGE*
    *Alleviated with libs like Immutable.js

    View full-size slide

  67. FIRST CLASS
    FUNCTIONS

    View full-size slide

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

    View full-size slide

  69. 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);

    View full-size slide

  70. 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);

    View full-size slide

  71. 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);

    View full-size slide

  72. ENCAPSULATION
    CLOSURES

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  76. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  79. FOUNDATION FOR
    HIGHER ORDER
    PATTERNS
    FIRST CLASS
    CLOSURES

    View full-size slide

  80. PARTIAL
    APPLICATION

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  99. PIECING IT
    TOGETHER

    View full-size slide

  100. 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));

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  104. 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));

    View full-size slide

  105. 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));

    View full-size slide

  106. 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));

    View full-size slide

  107. customRequest({ url: '/cart/items' })
    .then(map(pluck('price')))
    .then(map(discount))
    .then(map(tax));
    [
    { price: 5 },
    { price: 10 },
    { price: 3 },
    ]

    View full-size slide

  108. [
    { price: 5 },
    { price: 10 },
    { price: 3 },
    ]
    map(pluck('price'))
    [
    5,
    10,
    3,
    ]
    item.price

    View full-size slide

  109. [
    5,
    10,
    3,
    ]
    map(discount)
    [
    4.9,
    9.8,
    2.94,
    ]
    price * 0.98

    View full-size slide

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

    View full-size slide

  111. COMPOSING
    CLOSURES

    View full-size slide

  112. 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']

    View full-size slide

  113. 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']

    View full-size slide

  114. const processWord =
    compose(hyphenate, reverse, toUpperCase);
    const processWordExplicit = (word) => {
    return hyphenate(reverse(toUpperCase(word)));
    };

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  124. const compose = (...fns) => arg => (
    fns.reduceRight(
    (result, fn) => fn(result),
    arg
    )
    );
    fns = []
    result = 'OL-LEH'

    View full-size slide

  125. customRequest({ url: '/cart/items' })
    .then(map(pluck('price')))
    .then(map(discount))
    .then(map(tax));
    RETURNING TO PRICES EXAMPLE

    View full-size slide

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

    View full-size slide

  127. customRequest({ url: '/cart/items' })
    .then(map(
    compose(
    tax,
    discount,
    pluck('price')
    )
    ));
    Single
    iteration

    View full-size slide

  128. RECURSION SOLVE A PROBLEM
    IN TERMS OF ITSELF

    View full-size slide

  129. const factorial = (n) => {
    let result = 1;
    while (n > 1) {
    result *= n;
    n--;
    }
    return result;
    };

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  133. factorial(4);

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  141. STEPS
    Find the recurrence

    (n × n-1 × n-2 × … 1)

    Find the base case

    (n < 2)

    View full-size slide

  142. PERFORMANCE
    RECURSION

    View full-size slide

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

    View full-size slide

  144. const value = factorial(100000);
    console.log(value); // ???
    WHAT IS THE RESULT?
    RangeError: Maximum call
    stack size exceeded

    View full-size slide

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

    Call Stack

    View full-size slide

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

    factorial(4)
    Call Stack

    View full-size slide

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

    factorial(4)
    factorial(3)
    Call Stack

    View full-size slide

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

    factorial(4)
    factorial(3)
    factorial(2)
    Call Stack

    View full-size slide

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

    factorial(4)
    factorial(3)
    factorial(2)
    factorial(1)
    Call Stack

    View full-size slide

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

    factorial(4)
    factorial(3)
    factorial(2)
    Call Stack

    View full-size slide

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

    factorial(4)
    factorial(3)
    Call Stack

    View full-size slide

  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;

    factorial(4)
    Call Stack

    View full-size slide

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

    Call Stack

    View full-size slide

  154. 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

    View full-size slide

  155. IN ES2015!
    TAIL CALL
    OPTIMIZATION

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  167. const value = factorial(100000);
    console.log(value);
    // Infinity

    View full-size slide

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

    Call Stack

    View full-size slide

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

    factorial(4, 1)
    Call Stack

    View full-size slide

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

    factorial(3, 4)
    Call Stack

    View full-size slide

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

    factorial(2, 12)
    Call Stack

    View full-size slide

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

    factorial(1, 24)
    Call Stack

    View full-size slide

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

    Call Stack

    View full-size slide

  174. RECAP
    PREDICTABLE
    SAFE
    TRANSPARENT
    MODULAR

    View full-size slide

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

    View full-size slide

  176. ES6/7/LATER
    babeljs.io

    View full-size slide

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

    View full-size slide

  178. LIBRARIES
    Lodash (lodash.com)
    Ramda (ramdajs.com)
    Rx (reactivex.io)
    Bacon.js (baconjs.github.io)
    Immutable.js (facebook.github.io/immutable-js)

    View full-size slide

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

    View full-size slide

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

    View full-size slide