ConnectJS Functional Programming Basics in ES6

ConnectJS Functional Programming Basics in ES6

94bd558238b69c45d3d3e15797ae94f7?s=128

Jeremy Fairbank

October 16, 2015
Tweet

Transcript

  1. 5.

    What is a function? Relation that pairs each element in

    the domain with exactly one element in the range. Domain Range
  2. 14.

    Alonzo Church • Church-Turing Thesis • Lambda Calculus • Represent

    computation in terms of “function abstraction and application using variable binding and substitution.”
  3. 16.
  4. 17.

    Represent computation in terms of “function abstraction and application using

    variable binding and substitution.” λ-Calculus Bind “x” to function => parameters
  5. 18.

    Represent computation in terms of “function abstraction and application using

    variable binding and substitution.” λ-Calculus Substitution => apply arguments
  6. 30.

    – Wikipedia “In computer science, functional programming is a programming

    paradigm — 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. It is a declarative programming paradigm, which means programming is done with expressions.” What is Functional Programming?
  7. 32.

    Key Concepts • Declarative (vs. Imperative) • First Class Functions

    • Referential Transparency and Purity • Recursion • Immutability • Partial Application and Currying • Composition
  8. 34.

    const greeting = 'hello'; let n = 42; n =

    5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz']
  9. 35.

    var greeting = 'hello'; const greeting = 'hello'; let n

    = 42; n = 5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz']
  10. 36.

    var n = 42; n = 5; const greeting =

    'hello'; let n = 42; n = 5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz']
  11. 37.

    const greeting = 'hello'; let n = 42; n =

    5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz'] var add = function(x, y) { return x + y; }; var printAndReturn = function(string) { console.log(string); return string; };
  12. 38.

    const greeting = 'hello'; let n = 42; n =

    5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz'] function greet() { var g = arguments[0] === undefined ? 'Hi' : arguments[0]; console.log(g); }
  13. 39.

    const greeting = 'hello'; let n = 42; n =

    5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz'] function print(a) { var args = [].slice.call(arguments, 1); console.log.apply(console, [a].concat(args)); }
  14. 40.

    const greeting = 'hello'; let n = 42; n =

    5; const add = (x, y) => x + y; const printAndReturn = (string) => { console.log(string); return string; }; function greet(g = 'Hi') { console.log(g); } function print(a, ...args) { console.log(a, ...args); } const [x, ...y] = ['foo', 'bar', 'baz']; console.log(x); // foo console.log(y); // ['bar', 'baz'] var _ref = ['foo', 'bar', 'baz']; var x = _ref[0]; var y = _ref.slice(1); console.log(x); console.log(y);
  15. 42.

    Imperative • Most familiar style of programming • Telling the

    computer how to accomplish a task • Algorithms are a series of steps • Mutable data usually involved • C, C++, Java
  16. 43.

    Imperative function doubleNumbers(numbers) { const doubled = []; const l

    = numbers.length; for (let i = 0; i < l; i++) { let doubledNumber = numbers[i] * 2; doubled.push(doubledNumber); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]
  17. 44.

    Imperative function doubleNumbers(numbers) { const doubled = []; const l

    = numbers.length; for (let i = 0; i < l; i++) { let doubledNumber = numbers[i] * 2; doubled.push(doubledNumber); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]
  18. 45.

    Imperative function doubleNumbers(numbers) { const doubled = []; const l

    = numbers.length; for (let i = 0; i < l; i++) { let doubledNumber = numbers[i] * 2; doubled.push(doubledNumber); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]
  19. 46.

    Imperative function doubleNumbers(numbers) { const doubled = []; const l

    = numbers.length; for (let i = 0; i < l; i++) { let doubledNumber = numbers[i] * 2; doubled.push(doubledNumber); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]
  20. 47.

    Imperative function doubleNumbers(numbers) { const doubled = []; const l

    = numbers.length; for (let i = 0; i < l; i++) { let doubledNumber = numbers[i] * 2; doubled.push(doubledNumber); } return doubled; } doubleNumbers([1, 2, 3]); // [2, 4, 6]
  21. 48.

    Declarative • Typically associated with functional programming • Telling the

    computer what you want, not how to get it • Algorithms are compositions of functions • Immutable data (or minimal side effects) preferred • SQL, HTML, Haskell, DSLs
  22. 49.

    Declarative function doubleNumber(n) { return n * 2; } function

    doubleNumbers(numbers) { return numbers.map(doubleNumber); } doubleNumbers([1, 2, 3]); // [2, 4, 6]
  23. 51.

    First Class Functions Functions are expressions/values. // Function declaration function

    add(x, y) { return x + y; } // Assign to a variable const addAlias = add; // Assign an anonymous function expression const multiply = (x, y) => x * y; // Functions have properties console.log(add.name); // 'add' console.log(addAlias.name); // 'add' console.log(multiply.name); // ''
  24. 52.

    First Class Functions Functions are expressions/values. // Function declaration function

    add(x, y) { return x + y; } // Assign to a variable const addAlias = add; // Assign an anonymous function expression const multiply = (x, y) => x * y; // Functions have properties console.log(add.name); // 'add' console.log(addAlias.name); // 'add' console.log(multiply.name); // ''
  25. 53.

    First Class Functions Functions are expressions/values. // Function declaration function

    add(x, y) { return x + y; } // Assign to a variable const addAlias = add; // Assign an anonymous function expression const multiply = (x, y) => x * y; // Functions have properties console.log(add.name); // 'add' console.log(addAlias.name); // 'add' console.log(multiply.name); // ''
  26. 54.

    First Class Functions Functions are expressions/values. // Function declaration function

    add(x, y) { return x + y; } // Assign to a variable const addAlias = add; // Assign an anonymous function expression const multiply = (x, y) => x * y; // Functions have properties console.log(add.name); // 'add' console.log(addAlias.name); // 'add' console.log(multiply.name); // ''
  27. 56.

    First Class Functions Functions as arguments // Passing in anonymous

    functions myEventLib.on('update', (data) => console.log(data)); function doubleNumber(n) { return n * 2; } // Earlier example, passing in named function function doubleNumbers(numbers) { return numbers.map(doubleNumber); }
  28. 57.

    First Class Functions Functions as arguments // Passing in anonymous

    functions myEventLib.on('update', (data) => console.log(data)); function doubleNumber(n) { return n * 2; } // Earlier example, passing in named function function doubleNumbers(numbers) { return numbers.map(doubleNumber); }
  29. 59.

    First Class Functions Functions as return values // Return a

    new function that adds a number // to another function additionFactory(x) { return (y) => x + y; } const add1 = additionFactory(1); add1(2); // 3
  30. 60.
  31. 61.

    Closures • Allow functions to reference other scopes • “Close

    over” variables in higher scopes • Critical to functional programming paradigms like partial application
  32. 62.

    Closures // New function “closes” over x function additionFactory(x) {

    return (y) => x + y; } const add1 = additionFactory(1); add1(2); // 3
  33. 63.

    // New function “closes” over x function additionFactory(x) { return

    (y) => x + y; } const add1 = additionFactory(1); add1(2); // 3 Closures Same x
  34. 64.

    Closures let myValue = 'foo'; function closure() { console.log(myValue); }

    function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'
  35. 65.

    Closures let myValue = 'foo'; function closure() { console.log(myValue); }

    function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'
  36. 66.

    let myValue = 'foo'; function closure() { console.log(myValue); } function

    trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar' Closures
  37. 67.

    Closures let myValue = 'foo'; function closure() { console.log(myValue); }

    function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'
  38. 68.

    Closures let myValue = 'foo'; // Becomes 'hello world' function

    closure() { console.log(myValue); } function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'
  39. 69.

    Closures let myValue = 'foo'; // Now 'hello world' function

    closure() { console.log(myValue); } function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'
  40. 70.

    Closures let myValue = 'foo'; // Now 'hello world' function

    closure() { console.log(myValue); } function trickyClosure() { let myValue = 'bar'; console.log(myValue); } function anotherTrickyClosure() { myValue = 'hello world'; console.log(myValue); } closure(); // 'foo' trickyClosure(); // 'bar' closure(); // 'foo' anotherTrickyClosure(); // 'hello world' closure(); // 'hello world' trickyClosure(); // 'bar'
  41. 72.

    Referential Transparency • Fuzzy term associated with purity • For

    a given input, a function always returns the same value • Replace function calls with return value • No dependency on state outside function
  42. 73.

    Referential Transparency function doubleNumber(n) { return n * 2; }

    function square(n) { return n * n; } function add6(n) { return n + 6; } // Reduces down to the same value let value1 = add6(square(doubleNumber(3))); // 42 let value2 = add6(square(6)); // 42 let value3 = add6(36); // 42 let value4 = 42; // Same value for every call let otherValue1 = doubleNumber(3) + doubleNumber(3) + doubleNumber(3) + doubleNumber(3); let otherValue2 = 6 + 6 + 6 + 6;
  43. 74.

    Referential Transparency function doubleNumber(n) { return n * 2; }

    function square(n) { return n * n; } function add6(n) { return n + 6; } // Reduces down to the same value let value1 = add6(square(doubleNumber(3))); // 42 let value2 = add6(square(6)); // 42 let value3 = add6(36); // 42 let value4 = 42; // Same value for every call let otherValue1 = doubleNumber(3) + doubleNumber(3) + doubleNumber(3) + doubleNumber(3); let otherValue2 = 6 + 6 + 6 + 6;
  44. 75.

    Referential Transparency function doubleNumber(n) { return n * 2; }

    function square(n) { return n * n; } function add6(n) { return n + 6; } // Reduces down to the same value let value1 = add6(square(doubleNumber(3))); // 42 let value2 = add6(square(6)); // 42 let value3 = add6(36); // 42 let value4 = 42; // Same value for every call let otherValue1 = doubleNumber(3) + doubleNumber(3) + doubleNumber(3) + doubleNumber(3); let otherValue2 = 6 + 6 + 6 + 6;
  45. 76.

    Referential Transparency let counter = 0; function addToCounter(n) { counter++;

    return n + counter; } // Not referentially transparent let value1 = addToCounter(1) + addToCounter(1); // 5 let value2 = 2 + 3; // Not the same value every call!
  46. 78.

    Idempotency • Special case of referential transparency • Multiple function

    applications produce the same result as one application
  47. 79.

    Idempotency let abs = Math.abs; function identity(x) { return x;

    } function add2(n) { return n + 2; } // Idempotent and referentially transparent abs(abs(abs(abs(-1)))) === abs(-1); identity(identity(identity(42))) === identity(42); // Not idempotent, but referentially transparent add2(add2(add2(1))) !== add2(1); // 7 !== 3
  48. 80.

    Idempotency let abs = Math.abs; function identity(x) { return x;

    } function add2(n) { return n + 2; } // Idempotent and referentially transparent abs(abs(abs(abs(-1)))) === abs(-1); identity(identity(identity(42))) === identity(42); // Not idempotent, but referentially transparent add2(add2(add2(1))) !== add2(1); // 7 !== 3
  49. 81.

    Idempotency let abs = Math.abs; function identity(x) { return x;

    } function add2(n) { return n + 2; } // Idempotent and referentially transparent abs(abs(abs(abs(-1)))) === abs(-1); identity(identity(identity(42))) === identity(42); // Not idempotent, but referentially transparent add2(add2(add2(1))) !== add2(1); // 7 !== 3
  50. 82.
  51. 83.

    Purity • “Strict referential transparency” • No side effects •

    No global/shared state • No impure arguments • No I/O
  52. 84.

    Pure Functions function add(x, y) { return x + y;

    } function capitalize(string) { return string[0].toUpperCase() + string.slice(1).toLowerCase(); } function toTitleCase(string) { return string .split(/\s+/) .map(capitalize) .join(' '); }
  53. 85.

    Impure Functions let myName = 'Jeremy'; const myHobbies = ['programming',

    'reading', 'playing guitar']; function getName() { return myName; } function printName(name) { console.log(name); } function add(x, y) { myName = 'Joe'; return x + y; } function hobbiesMapper(hobbies) { return (fn) => hobbies.map(fn); }
  54. 86.

    let myName = 'Jeremy'; const myHobbies = ['programming', 'reading', 'playing

    guitar']; function getName() { return myName; } function printName(name) { console.log(name); } function add(x, y) { myName = 'Joe'; return x + y; } function hobbiesMapper(hobbies) { return (fn) => hobbies.map(fn); } Impure Functions Accesses global state
  55. 87.

    let myName = 'Jeremy'; const myHobbies = ['programming', 'reading', 'playing

    guitar']; function getName() { return myName; } function printName(name) { console.log(name); } function add(x, y) { myName = 'Joe'; return x + y; } function hobbiesMapper(hobbies) { return (fn) => hobbies.map(fn); } Impure Functions Prints to stdout
  56. 88.

    let myName = 'Jeremy'; const myHobbies = ['programming', 'reading', 'playing

    guitar']; function getName() { return myName; } function printName(name) { console.log(name); } function add(x, y) { myName = 'Joe'; return x + y; } function hobbiesMapper(hobbies) { return (fn) => hobbies.map(fn); } Impure Functions Modifies global state
  57. 89.

    let myName = 'Jeremy'; const myHobbies = ['programming', 'reading', 'playing

    guitar']; function getName() { return myName; } function printName(name) { console.log(name); } function add(x, y) { myName = 'Joe'; return x + y; } function hobbiesMapper(hobbies) { return (fn) => hobbies.map(fn); } Impure Functions Array may mutate
  58. 90.
  59. 91.

    Recursion • Defining a solution to a problem in terms

    of itself • Solve the problem by solving smaller pieces • Similar to inductive proofs • In programming, a function that calls itself • In FP, imperative looping (e.g. with while or for) is practically nonexistent, so solve with recursion
  60. 93.

    Recursion: Factorial function factorial(n) { let result = 1; while

    (n > 1) { result *= n--; } return result; } Imperative
  61. 94.

    Recursion: Factorial function factorial(n) { let result = 1; while

    (n > 1) { result *= n--; } return result; } Imperative function factorial(n) { if (n < 2) { return 1; } return n * factorial(n - 1); } Recursive
  62. 95.

    Recursion: Factorial function factorial(n) { if (n < 2) {

    return 1; } return n * factorial(n - 1); }
  63. 96.

    Recursion: Factorial function factorial(n) { if (n < 2) {

    return 1; } return n * factorial(n - 1); } Base case
  64. 97.

    Recursion: Factorial function factorial(n) { if (n < 2) {

    return 1; } return n * factorial(n - 1); } Recursive call
  65. 102.

    Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 *

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

    Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 *

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

    Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 *

    factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2;
  68. 105.

    Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 *

    factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6;
  69. 106.

    Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 *

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

    Recursion: Factorial function factorial(n) { if (n < 2) {

    return 1; } return n * factorial(n - 1); }
  71. 111.

    Recursion: Factorial function factorial(n) { if (n < 2) {

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

    Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 *

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

    Recursion: Factorial 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> Stack
  74. 114.

    Recursion: Factorial 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) Stack
  75. 115.

    Recursion: Factorial <main> factorial(4) factorial(3) factorial(4); 4 * factorial(3); 4

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

    Recursion: Factorial <main> factorial(4) factorial(3) 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(2) Stack
  77. 117.

    Recursion: Factorial <main> factorial(4) factorial(3) 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(2) factorial(1) Stack
  78. 118.

    Recursion: Factorial <main> factorial(4) factorial(3) factorial(2) factorial(4); 4 * factorial(3);

    4 * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; Stack
  79. 119.

    Recursion: Factorial <main> factorial(4) factorial(3) factorial(4); 4 * factorial(3); 4

    * 3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; Stack
  80. 120.

    Recursion: Factorial <main> factorial(4) factorial(4); 4 * factorial(3); 4 *

    3 * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; Stack
  81. 121.

    Recursion: Factorial <main> factorial(4); 4 * factorial(3); 4 * 3

    * factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2; 4 * 6; 24; Stack
  82. 122.

    Recursion Performance let value = factorial(100000); console.log(value); // ??? 100,000

    calls = 100,000 stack frames 1 stack frame ≈ 48B Max stack usage ≈ 1MB 100,000 × 48 / 1024 / 1024 = 4.58mb > 1mb
  83. 125.

    Tail Call Optimization • Optimize recursive functions to not exhaust

    max stack size • Replace stack frames instead of adding new ones • The last statement before returning must be recursive call • Typically rewrite function to include an accumulator that holds the current result • (Coming to JavaScript in ES2015!)
  84. 126.

    Tail Call Optimization Unoptimizable function factorial(n) { if (n <

    2) { return 1; } return n * factorial(n - 1); }
  85. 127.

    Tail Call Optimization Unoptimizable function factorial(n) { if (n <

    2) { return 1; } return n * factorial(n - 1); } 1
  86. 128.

    Tail Call Optimization Unoptimizable function factorial(n) { if (n <

    2) { return 1; } return n * factorial(n - 1); } 1 2
  87. 129.

    Tail Call Optimization Unoptimizable function factorial(n) { if (n <

    2) { return 1; } return n * factorial(n - 1); } 1 3 2
  88. 130.

    Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); }
  89. 131.

    Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); } Current value accumulator
  90. 132.

    Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); } Return the accumulator for base case
  91. 133.

    Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); } Move calculation inside the call
  92. 134.

    Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); } 1
  93. 135.

    Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); } 1 2
  94. 136.

    Optimize Factorial function factorial(n, accum = 1) { if (n

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

    Factorial TCO <main> factorial(4, 1) Stack factorial(4 /*, 1 */);

    factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;
  96. 141.

    Factorial TCO <main> factorial(3, 4) Stack factorial(4 /*, 1 */);

    factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;
  97. 142.

    Factorial TCO <main> factorial(2, 12) Stack factorial(4 /*, 1 */);

    factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;
  98. 143.

    Factorial TCO <main> factorial(1, 24) Stack factorial(4 /*, 1 */);

    factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;
  99. 144.
  100. 149.

    Recursion: Fibonacci function fibonacci(n) { if (n < 2) {

    return n; } return fibonacci(n - 1) + fibonacci(n - 2); }
  101. 150.

    Recursion: Fibonacci function fibonacci(n) { if (n < 2) {

    return n; } return fibonacci(n - 1) + fibonacci(n - 2); } Base Cases
  102. 151.

    Recursion: Fibonacci function fibonacci(n) { if (n < 2) {

    return n; } return fibonacci(n - 1) + fibonacci(n - 2); } Recurrence
  103. 152.

    Recursion: Fibonacci fib(0); // 0 fib(1); // 1 fib(2); //

    1 fib(3); // 2 fib(4); // 3 fib(5); // 5 function fibonacci(n) { if (n < 2) { return n; } return fibonacci(n - 1) + fibonacci(n - 2); }
  104. 156.

    Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms

    fibonacci(20); // 0 ms fibonacci(30); // 13 ms
  105. 157.

    Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms

    fibonacci(20); // 0 ms fibonacci(30); // 13 ms fibonacci(35); // 131 ms
  106. 158.

    Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms

    fibonacci(20); // 0 ms fibonacci(30); // 13 ms fibonacci(35); // 131 ms fibonacci(40); // 1516 ms
  107. 159.

    Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms

    fibonacci(20); // 0 ms fibonacci(30); // 13 ms fibonacci(35); // 131 ms fibonacci(40); // 1516 ms fibonacci(45); // 16.6 seconds!
  108. 160.

    Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms

    fibonacci(20); // 0 ms fibonacci(30); // 13 ms fibonacci(35); // 131 ms fibonacci(40); // 1516 ms fibonacci(45); // 16.6 seconds! fibonacci(100); // Who knows how long
  109. 161.

    Exponential Growth Runtime (ms) 0 4500 9000 13500 18000 Nth

    number of Fibonacci Sequence 0 10 20 30 40 50
  110. 165.

    Recursive Call Tree Computing some of the same values several

    times! fibonacci(4) x 2 fibonacci(3) x 3
  111. 166.

    Recursive Call Tree Computing some of the same values several

    times! fibonacci(4) x 2 fibonacci(3) x 3 fibonacci(2) x 5
  112. 167.

    Recursive Call Tree Computing some of the same values several

    times! fibonacci(4) x 2 fibonacci(3) x 3 fibonacci(2) x 5 fibonacci(1) x 3
  113. 169.

    TCO and Dynamic Programming • Build up the result from

    the leaves of the tree • Avoids recalculating the same values • Make recursive calls in the reverse direction
  114. 170.

    Fibonacci TCO function fibonacci(n, current = 0, next = 1)

    { if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); }
  115. 171.

    Fibonacci TCO function fibonacci(n, current = 0, next = 1)

    { if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); } Two accumulators
  116. 172.

    Fibonacci TCO function fibonacci(n, current = 0, next = 1)

    { if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); } Results for next call
  117. 173.

    Fibonacci TCO function fibonacci(n, current = 0, next = 1)

    { if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); } Reach the end, so return whatever was built up
  118. 174.

    Fibonacci TCO fibonacci(6 /*, 0, 1 */); fibonacci(5, 1, 1);

    fibonacci(4, 1, 2); fibonacci(3, 2, 3); fibonacci(2, 3, 5); fibonacci(1, 5, 8); fibonacci(0, 8, 13); 8;
  119. 175.

    Fibonacci TCO fibonacci(6 /*, 0, 1 */); fibonacci(5, 1, 1);

    fibonacci(4, 1, 2); fibonacci(3, 2, 3); fibonacci(2, 3, 5); fibonacci(1, 5, 8); fibonacci(0, 8, 13); 8; Fibonacci sequence
  120. 176.

    Fibonacci Performance fibonacci(1); // 0 ms fibonacci(5); // 0 ms

    fibonacci(20); // 0 ms fibonacci(30); // 0 ms fibonacci(35); // 0 ms fibonacci(40); // 0 ms fibonacci(45); // 0 ms fibonacci(100); // 0 ms
  121. 178.

    Partial Application • Assign values to function parameters without evaluating

    the function (i.e. “prefill” argument values) • Produce a new function that takes in any remaining unassigned arguments
  122. 179.

    Partial Application // Before we would use this function additionFactory(x)

    { return (y) => x + y; } let add1 = additionFactory(1); add1(2); // 3 // Now we can write an add function // and use partial application function add(x, y) { return x + y; } // Partial application with native `bind` function add1 = add.bind(null, 1); add1(2); // 3 // Use a custom-written `partial` function add1 = partial(add, 1); add1(2); // 3
  123. 180.

    Partial Application // Before we would use this function additionFactory(x)

    { return (y) => x + y; } let add1 = additionFactory(1); add1(2); // 3 // Now we can write an add function // and use partial application function add(x, y) { return x + y; } // Partial application with native `bind` function add1 = add.bind(null, 1); add1(2); // 3 // Use a custom-written `partial` function add1 = partial(add, 1); add1(2); // 3
  124. 181.

    Partial Application // Before we would use this function additionFactory(x)

    { return (y) => x + y; } let add1 = additionFactory(1); add1(2); // 3 // Now we can write an add function // and use partial application function add(x, y) { return x + y; } // Partial application with native `bind` function add1 = add.bind(null, 1); add1(2); // 3 // Use a custom-written `partial` function add1 = partial(add, 1); add1(2); // 3
  125. 182.

    Partial Application // Before we would use this function additionFactory(x)

    { return (y) => x + y; } let add1 = additionFactory(1); add1(2); // 3 // Now we can write an add function // and use partial application function add(x, y) { return x + y; } // Partial application with native `bind` function add1 = add.bind(null, 1); add1(2); // 3 // Use a custom-written `partial` function add1 = partial(add, 1); add1(2); // 3
  126. 184.

    Partial Application function partial(fn, ...args) { return (...otherArgs) => {

    return fn(...args, ...otherArgs); }; } Applied arguments
  127. 185.

    Partial Application function partial(fn, ...args) { return (...otherArgs) => {

    return fn(...args, ...otherArgs); }; } Return new closure
  128. 186.

    Partial Application function partial(fn, ...args) { return (...otherArgs) => {

    return fn(...args, ...otherArgs); }; } Remaining arguments
  129. 187.

    Partial Application function partial(fn, ...args) { return (...otherArgs) => {

    return fn(...args, ...otherArgs); }; } Call original function with all arguments
  130. 188.

    Partial Application: Why? • Build up functions from smaller pieces

    • Remove duplication • Generalize function abstraction (i.e. no additionFactory type functions)
  131. 189.

    Partial Application: Why? function multiply(x, y) { return x *

    y; } const doubleNumber = partial(multiply, 2); const numbers = [1, 2, 3]; const doubledNumbers = map(doubleNumber, numbers); console.log(doubledNumbers); // [2, 4, 6]
  132. 190.
  133. 191.

    Currying • Similar to partial application • Bakes partial application

    into a function • Successive invocations of function with arguments returns a new function with those arguments assigned • Keep returning a new function until all arguments “filled,” and then invoke the actual function with all of those arguments (sounds recursive, huh?)
  134. 192.

    Currying const add = curry((x, y, z) => x +

    y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6
  135. 193.

    Currying const add = curry((x, y, z) => x +

    y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6
  136. 194.

    Currying const add = curry((x, y, z) => x +

    y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6
  137. 195.

    Currying const add = curry((x, y, z) => x +

    y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6
  138. 196.

    Currying const add = curry((x, y, z) => x +

    y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6
  139. 197.

    Currying const add = curry((x, y, z) => x +

    y + z); // All arguments supplied, normal invocation. add(1, 2, 3); // 6 // Supply arguments one at a time. Final argument // induces invocation. add(1)(2)(3); // 6 // Equivalent to previous example. let add1 = add(1); let add3 = add1(2); add3(3); // 6 // Supply more than one argument at a time. add(1, 2)(3); // 6 add(1)(2, 3); // 6
  140. 198.

    Currying function curry(fn, len = fn.length) { return (...args) =>

    { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; }
  141. 199.

    Currying function curry(fn, len = fn.length) { return (...args) =>

    { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Knowing arity is important!
  142. 200.

    Currying function curry(fn, len = fn.length) { return (...args) =>

    { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Arguments to apply (or invoke)
  143. 201.

    Currying function curry(fn, len = fn.length) { return (...args) =>

    { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Recursive call if not all arguments supplied
  144. 202.

    Currying function curry(fn, len = fn.length) { return (...args) =>

    { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Curry the partially applied function
  145. 203.

    Currying function curry(fn, len = fn.length) { return (...args) =>

    { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Arity decreases
  146. 204.

    Currying function curry(fn, len = fn.length) { return (...args) =>

    { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; } Base case: all arguments supplied
  147. 205.

    Currying: Why? let curriedMap = curry(map); let multiply = curry((x,

    y) => x * y); let doubleNumbers = curriedMap(multiply(2)); let numbers = [1, 2, 3]; console.log(doubleNumbers(numbers)); // [2, 4, 6]
  148. 208.

    Function Composition • Compose functions together to form new functions

    • Pipe function output to next function • Helps enforce modularity/ SOC by keeping functions small
  149. 209.

    Function Composition let multiply = curry((x, y) => x *

    y); let multiply2 = multiply(2); let multiply3 = multiply(3); let multiply6 = compose(multiply2, multiply3); 2 * 3 * 6 === multiply6(6); // 36 let add = curry((x, y) => x + y); let subtract = curry((y, x) => -y + x); let add5 = compose(add(6), add(1), subtract(2)); 3 - 2 + 6 + 1 === add5(3); // 8
  150. 210.

    Function Composition let multiply = curry((x, y) => x *

    y); let multiply2 = multiply(2); let multiply3 = multiply(3); let multiply6 = compose(multiply2, multiply3); 2 * 3 * 6 === multiply6(6); // 36 let add = curry((x, y) => x + y); let subtract = curry((y, x) => -y + x); let add5 = compose(add(6), add(1), subtract(2)); 3 - 2 + 6 + 1 === add5(3); // 8
  151. 211.

    Function Composition let multiply = curry((x, y) => x *

    y); let multiply2 = multiply(2); let multiply3 = multiply(3); let multiply6 = compose(multiply2, multiply3); 2 * 3 * 6 === multiply6(6); // 36 let add = curry((x, y) => x + y); let subtract = curry((y, x) => -y + x); let add5 = compose(add(6), add(1), subtract(2)); 3 - 2 + 6 + 1 === add5(3); // 8
  152. 212.

    Function Composition function compose(...fns) { return (...args) => { const

    result = fns.reduceRight((memo, fn) => { return [fn(...memo)]; }, args); return result[0]; }; } function compose(f, g) { return (...args) => f(g(...args)); }
  153. 213.

    Function Composition function compose(...fns) { return (...args) => { const

    result = fns.reduceRight((memo, fn) => { return [fn(...memo)]; }, args); return result[0]; }; } function compose(f, g) { return (...args) => f(g(...args)); }
  154. 214.

    Function Composition function compose(...fns) { return (...args) => { const

    result = fns.reduceRight((memo, fn) => { return [fn(...memo)]; }, args); return result[0]; }; } function compose(f, g) { return (...args) => f(g(...args)); }
  155. 215.

    Function Composition function compose(...fns) { return (...args) => { const

    result = fns.reduceRight((memo, fn) => { return [fn(...memo)]; }, args); return result[0]; }; } function compose(f, g) { return (...args) => f(g(...args)); }
  156. 216.

    Function Composition function compose(...fns) { return (...args) => { const

    result = fns.reduceRight((memo, fn) => { return [fn(...memo)]; }, args); return result[0]; }; } function compose(f, g) { return (...args) => f(g(...args)); }
  157. 219.

    Why FP • Concise, elegant solutions to problems • Modularity

    and composition • Eliminate data races with immutability • Unit tests are simpler — no checking for state changes
  158. 220.

    • Prof. Frisby’s Mostly Adequate…
 (github.com/MostlyAdequate/mostly-adequate-guide) • Babel (babeljs.io) •

    Lodash (lodash.com) • Ramda (ramdajs.com) • Clojurescript (github.com/clojure/clojurescript) • Immutable.js (github.com/facebook/immutable-js) • React (facebook.github.io/react/) • Redux (redux.js.org)