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

Functional JavaScript for Beginners

Steve Kinney
May 28, 2016
120

Functional JavaScript for Beginners

Steve Kinney

May 28, 2016
Tweet

Transcript

  1. · What functional programming even is · Functions as first-class

    objects · Pure versus impure functions · Partial application · Currying
  2. A style of building the structure and elements of computer

    programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. — Wikipedia
  3. A pure function is one that takes arguments and returns

    a value without having any side effects.
  4. const add = function (a, b) { return a +

    b; }; This is a pure function.
  5. const randomNumbers = []; const addARandomNumber = function () {

    randomNumbers.push(Math.random()); }; This is an impure function.
  6. let result; function capitalizeWord(input) { var counter; var inputArray =

    input.split(" "); var transformed = ""; result = []; for (counter = 0; counter < inputArray.length; counter++) { transformed = [ inputArray[counter].charAt(0).toUpperCase(), inputArray[counter].substring(1) ].join(""); result.push(transformed); } }; (Don't even try to grok this.)
  7. const capitalize = function (input) { return [input.charAt(0).toUpperCase(), input.substring(1)].join(''); }

    const processWords = function (fn, string) { return str.split(" ").map(fn).join(" "); } processWords(capitalize, 'hello world');
  8. const veryImportantNumbers = [1, 4, 15]; Let's say we have

    some very important numbers and we want to double them.
  9. const veryImportantNumbers = [1, 4, 15]; const doubledNumbers = [];

    for (let i; i < veryImportantNumbers.length; i++) { const number = veryImportantNumbers[i]; doubledNumbers.push(number); }
  10. map · takes an array of things, · runs each

    thing through a (hopefully) pure function, · returns a new array of things
  11. const veryImportantNumbers = [1, 4, 15]; const double = (n)

    => n * 2; const doubledNumbers = veryImportantNumbers.map(double);
  12. const veryImportantNumbers = [1, 4, 15]; const double = function

    (n) { return n * 2 }; const doubledNumbers = veryImportantNumbers.map(double);
  13. const veryImportantNumbers = [1, 4, 15]; const double = (n)

    => n * 2; const doubledNumbers = veryImportantNumbers.map(double);
  14. const veryImportantNumbers = [1, 4, 15]; const double = (n)

    => n * 2; const square = (n) => n * n; const halve = (n) => n / 2; const addOne = (n) => n + 1; const doubledNumbers = veryImportantNumbers.map(double); const squaredNumbers = veryImportantNumbers.map(square); const halvedNumbers = veryImportantNumbers.map(halve); const numbersPlusOne = veryImportantNumbers.map(addOne);
  15. reduce · Takes an array of things · Set ups

    an "accumulator" · Passes each one into a (hopefully) pure function · Returns the accumulator
  16. const veryImportantNumbers = [1, 4, 15]; let sum = 0;

    for (let i; i < veryImportantNumbers.length; i++) { sum += veryImportantNumbers[i];; }
  17. const veryImportantNumbers = [1, 4, 15]; const add = (a,

    b) => a + b; const sum = veryImportantNumbers.reduce(add);
  18. const veryImportantNumbers = [1, 4, 15]; const add = (a,

    b) => a + b; const multiply = (a, b) => a * b; const sum = veryImportantNumbers.reduce(add); const factorial = veryImportantNumbers.reduce(multiply);
  19. const veryImportantNumbers = [1, 4, 15]; let doubleSum = 0;

    for (let i; i < veryImportantNumbers.length; i++) { doubleSum += veryImportantNumbers[i] * 2; } This whole process is "tightly coupled."
  20. const veryImportantNumbers = [1, 4, 15]; const double = (n)

    => n * 2; const add = (a, b) => a + b; const doubleSum = veryImportantNumbers.map(double).reduce(add);
  21. const veryImportantNumbers = [1, 4, 15]; const map = _.map;

    const reduce = _.reduce; const double = (n) => n * 2; const add = (a, b) => a + b; const doubleSum = double(reduce(veryImportantNumbers, add));
  22. We can also solve this problem using Lodash's chain feature.

    const veryImportantNumbers = [1, 4, 15]; const { map, reduce, chain } = _; const double = (n) => n * 2; const add = (a, b) => a + b; chain(veryImportantNumbers).map(double).reduce(add).value(); But, it only really works with Lodash's functions.
  23. Unless you engage in some tomfoolery. const veryImportantNumbers = [1,

    4, 15]; const { map, reduce, chain } = _; const double = (n) => n * 2; const add = (a, b) => a + b; _.mixin({ double }); chain(veryImportantNumbers).reduce(add).double().value();
  24. Partial application is a technique where we provide some of

    the arguments to a function now and the rest later.
  25. This is useful because it allows us to use functions

    as templates for other functions.
  26. const add = (a, b) => a + b; const

    addOne = add.bind(this, 1); const subtractFive = add.bind(this, -5); addOne(5); // returns 6 subtractFive(12); // returns 7
  27. Confusingly, bind() takes a context as the first argument. It

    then uses the second argument to partially apply the first argument to the function you're calling it on.
  28. const add = (a, b) => a + b; const

    addOne = add.bind(this, 1); const addOne = add.bind(null, 1); const addOne = add.bind('sandwich', 1); If you're not using this in the function, then that first argument can be anything.
  29. const superAdd = (a, b, c, d) => a +

    b + c + d; const oneArgumentApplied = superAdd.bind(this, 1); // ! — waiting on b, c, and d. const twoArgumentsApplied = superAdd.bind(this, 1, 2); // ! — waiting on c and d. const threeArgumentsApplied = superAdd.bind(this, 1, 2, 3); // ! — waiting on d. const fourArgumentsApplied = superAdd.bind(this, 1, 2, 3, 4); // ! — ready to call with no additional arguments.
  30. const superAdd = (a, b, c, d) => a +

    b + c + d; const oneArgumentApplied = (b, c, d) => superAdd(1, b, c, d); const twoArgumentsApplied = (c, d) => superAdd(1, 2, c, d); const threeArgumentsApplied = (d) => superAdd(1, 2, 3, d); const fourArgumentsApplied = () => superAdd(1, 2, 3, 4);
  31. const add = (a, b) => a + b; const

    addOne = (b) => add(1, b); const subtractFive = (b) => add(-5, b); addOne(5); // returns 6 subtractFive(12); // returns 7
  32. const superAdd = (a, b, c, d) => a +

    b + c + d; const oneArgumentApplied = superAdd.bind(this, 1); const twoArgumentsApplied = oneArgumentApplied.bind(this, 2); const threeArgumentsApplied = twoArgumentsApplied.bind(this, 3); const fourArgumentsApplied = threeArgumentsApplied.bind(this, 4);
  33. const addThreeNumbers = (a, b, c) => a + b

    + c; addThreeNumbers(1, 2, 3); // returns 6 This is a non-curried function.
  34. const curriedAdd = (a) => { return (b) => {

    return (c) => { return a + b + c; } } }; curriedAdd(1)(2)(3); // returns 6
  35. const firstArgumentApplied = curriedAdd(1); // returns a function const secondArgumentApplied

    = firstArgumentApplied(2); // returns a function const thirdArgumentApplied = secondArgumentApplied(3); // returns 6
  36. Let's say it was out job to make a bunch

    of functions that took some text and wrapped them in HTML tags.
  37. const wrapInParagraphTags = (body) => '<p>' + body + '</p>';

    const wrapInHeaderTags = (body) => '<h1>' + body + '</h1>'; const wrapInListItemTags = (body) => '<li>' + body + '</li>'; (And like a hundred other functions.)
  38. const createWrapper = (tag) => { return (body) => {

    return `<${tag}>${body}</${tag}>` }; } const wrapInParagraphTags = createWrapper('p'); const p = wrapInParagraphTags('Currying FTW!'); // ! returns `<p>Currying FTW!</p>` const h2 = createWrapper('h2')('Hello world'); // ! returns `<h2>Hello world</h2>`
  39. const wrapInTags = (tag, body) => `<${tag}>${body}</${tag}>`; const wrapInParagraphTags =

    wrapInTags.bind(null, 'p'); const p = wrapInParagraphTags('Partial application FTW!'); const h2 = wrapInTags('h2', 'Hello world');