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

Functional JavaScript for Beginners

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Steve Kinney Steve Kinney
May 28, 2016
130

Functional JavaScript for Beginners

Avatar for Steve Kinney

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');