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

Scenic City Summit: Functional Programming Basics in ES6

Scenic City Summit: Functional Programming Basics in ES6

Jeremy Fairbank

August 12, 2016
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. sigient.com
    Your website, SimplyBuilt.
    simplybuilt.com

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  5. Domain to Range

    View full-size slide

  6. Domain Range

    View full-size slide

  7. PURE & DECLARATIVE
    PREDICTABLE

    View full-size slide

  8. IMMUTABLE STATE
    SAFE

    View full-size slide

  9. FIRST CLASS STATE
    TRANSPARENT

    View full-size slide

  10. COMPOSABLE FIRST
    CLASS CLOSURES
    MODULAR

    View full-size slide

  11. All good
    let age = 28;
    age = 29;
    const name = 'Jeremy';
    name = 'Jet';
    Syntax error

    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 array = (...elements) => {
    return elements;
    };
    array(1, 2, 3); // [1, 2, 3]
    const log = (...args) => {
    console.log(...args);
    };
    log('hello', 'scenic city summit');
    // hello scenic city summit

    View full-size slide

  15. const array = (...elements) => {
    return elements;
    };
    array(1, 2, 3); // [1, 2, 3]
    const log = (...args) => {
    console.log(...args);
    };
    log('hello', 'scenic city summit');
    // hello scenic city summit

    View full-size slide

  16. 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;

    View full-size slide

  17. 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;

    View full-size slide

  18. const greet = (name, greeting = 'Hi') => {
    console.log(greeting, name);
    };
    greet('Scenic City Summit', 'Hello');
    // Hello Scenic City Summit
    greet('Chattanooga');
    // Hi Chattanooga

    View full-size slide

  19. Object.assign(
    {},
    { hello: 'Chattanooga' },
    { hi: 'Scenic City Summit' }
    );
    // {
    // hello: 'Chattanooga',
    // hi: 'Scenic City Summit'
    // }

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

  25. HIDDEN STATE IS
    UNCERTAIN STATE

    View full-size slide

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

  27. HOW TO ACHIEVE THE
    RESULT
    IMPERATIVE

    View full-size slide

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

  29. DECLARE WHAT THE
    DESIRED RESULT IS
    DECLARATIVE

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  32. CREATE STATE, DON’T
    MUTATE IT
    IMMUTABLE

    View full-size slide

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

    View full-size slide

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

  35. Object.freeze
    Immutable.js

    View full-size slide

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

    View full-size slide

  37. FREE YOUR
    STATE

    View full-size slide

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

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

  40. PROS
    SAFETY
    FREE UNDO/REDO LOGS — REDUX
    EXPLICIT FLOW OF DATA
    LESS MEMORY USAGE*
    CONCURRENCY SAFETY*
    *In certain cases

    View full-size slide

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

    View full-size slide

  42. FIRST CLASS
    FUNCTIONS

    View full-size slide

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

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

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

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

  47. ENCAPSULATION
    CLOSURES

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

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

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

  54. FOUNDATION FOR
    HIGHER ORDER
    PATTERNS
    FIRST CLASS
    CLOSURES

    View full-size slide

  55. PARTIAL
    APPLICATION

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  72. PIECING IT
    TOGETHER

    View full-size slide

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

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

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

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

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

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

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  84. COMPOSING
    CLOSURES

    View full-size slide

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

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  95. RECURSION SOLVE A PROBLEM
    IN TERMS OF ITSELF

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  100. factorial(4);

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

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

  108. STEPS
    Find the recurrence

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

    Find the base case

    (n < 2)

    View full-size slide

  109. PERFORMANCE
    RECURSION

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

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

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

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

  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;

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

    View full-size slide

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

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

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

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

  122. IN ES2015!
    TAIL CALL
    OPTIMIZATION

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    Call Stack

    View full-size slide

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

    factorial(4, 1)
    Call Stack

    View full-size slide

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

    factorial(3, 4)
    Call Stack

    View full-size slide

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

    factorial(2, 12)
    Call Stack

    View full-size slide

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

    factorial(1, 24)
    Call Stack

    View full-size slide

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

    Call Stack

    View full-size slide

  141. RECAP
    PREDICTABLE
    SAFE
    TRANSPARENT
    MODULAR

    View full-size slide

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

    View full-size slide

  143. ES6/7/LATER
    babeljs.io

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide