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

ConnectJS Functional Programming Basics in ES6

ConnectJS Functional Programming Basics in ES6

Jeremy Fairbank

October 16, 2015
Tweet

More Decks by Jeremy Fairbank

Other Decks in Programming

Transcript

  1. What is a function? Relation that pairs each element in

    the domain with exactly one element in the range. Domain Range
  2. Alonzo Church • Church-Turing Thesis • Lambda Calculus • Represent

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

    variable binding and substitution.” λ-Calculus Bind “x” to function => parameters
  4. Represent computation in terms of “function abstraction and application using

    variable binding and substitution.” λ-Calculus Substitution => apply arguments
  5. – 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?
  6. Key Concepts • Declarative (vs. Imperative) • First Class Functions

    • Referential Transparency and Purity • Recursion • Immutability • Partial Application and Currying • Composition
  7. 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']
  8. 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']
  9. 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']
  10. 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; };
  11. 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); }
  12. 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)); }
  13. 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);
  14. 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
  15. 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]
  16. 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. 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. 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. 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. 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
  21. Declarative function doubleNumber(n) { return n * 2; } function

    doubleNumbers(numbers) { return numbers.map(doubleNumber); } doubleNumbers([1, 2, 3]); // [2, 4, 6]
  22. 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); // ''
  23. 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. 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. 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. 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); }
  27. 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. 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
  29. Closures • Allow functions to reference other scopes • “Close

    over” variables in higher scopes • Critical to functional programming paradigms like partial application
  30. Closures // New function “closes” over x function additionFactory(x) {

    return (y) => x + y; } const add1 = additionFactory(1); add1(2); // 3
  31. // New function “closes” over x function additionFactory(x) { return

    (y) => x + y; } const add1 = additionFactory(1); add1(2); // 3 Closures Same x
  32. 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'
  33. 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'
  34. 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
  35. 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. 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'
  37. 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'
  38. 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'
  39. 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
  40. 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;
  41. 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;
  42. 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. 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!
  44. Idempotency • Special case of referential transparency • Multiple function

    applications produce the same result as one application
  45. 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
  46. 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
  47. 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. Purity • “Strict referential transparency” • No side effects •

    No global/shared state • No impure arguments • No I/O
  49. 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(' '); }
  50. 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); }
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. Recursion: Factorial function factorial(n) { let result = 1; while

    (n > 1) { result *= n--; } return result; } Imperative
  57. 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
  58. Recursion: Factorial function factorial(n) { if (n < 2) {

    return 1; } return n * factorial(n - 1); }
  59. Recursion: Factorial function factorial(n) { if (n < 2) {

    return 1; } return n * factorial(n - 1); } Base case
  60. Recursion: Factorial function factorial(n) { if (n < 2) {

    return 1; } return n * factorial(n - 1); } Recursive call
  61. Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 *

    factorial(2); 4 * 3 * 2 * factorial(1);
  62. Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 *

    factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1;
  63. Recursion: Factorial factorial(4); 4 * factorial(3); 4 * 3 *

    factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2;
  64. 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;
  65. 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;
  66. Recursion: Factorial function factorial(n) { if (n < 2) {

    return 1; } return n * factorial(n - 1); }
  67. Recursion: Factorial function factorial(n) { if (n < 2) {

    return 1; } return n * factorial(n - 1); }
  68. 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;
  69. 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
  70. 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
  71. 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
  72. 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
  73. 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
  74. 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
  75. 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. 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
  77. 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
  78. 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
  79. 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!)
  80. Tail Call Optimization Unoptimizable function factorial(n) { if (n <

    2) { return 1; } return n * factorial(n - 1); }
  81. Tail Call Optimization Unoptimizable function factorial(n) { if (n <

    2) { return 1; } return n * factorial(n - 1); } 1
  82. Tail Call Optimization Unoptimizable function factorial(n) { if (n <

    2) { return 1; } return n * factorial(n - 1); } 1 2
  83. Tail Call Optimization Unoptimizable function factorial(n) { if (n <

    2) { return 1; } return n * factorial(n - 1); } 1 3 2
  84. Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); }
  85. Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); } Current value accumulator
  86. Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); } Return the accumulator for base case
  87. Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); } Move calculation inside the call
  88. Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); } 1
  89. Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); } 1 2
  90. Optimize Factorial function factorial(n, accum = 1) { if (n

    < 2) { return accum; } return factorial(n - 1, n * accum); } 1 3 2
  91. Factorial TCO <main> factorial(4, 1) Stack factorial(4 /*, 1 */);

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

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

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

    factorial(3, 4); factorial(2, 12); factorial(1, 24); 24;
  95. Recursion: Fibonacci function fibonacci(n) { if (n < 2) {

    return n; } return fibonacci(n - 1) + fibonacci(n - 2); }
  96. Recursion: Fibonacci function fibonacci(n) { if (n < 2) {

    return n; } return fibonacci(n - 1) + fibonacci(n - 2); } Base Cases
  97. Recursion: Fibonacci function fibonacci(n) { if (n < 2) {

    return n; } return fibonacci(n - 1) + fibonacci(n - 2); } Recurrence
  98. 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); }
  99. Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms

    fibonacci(20); // 0 ms fibonacci(30); // 13 ms
  100. Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms

    fibonacci(20); // 0 ms fibonacci(30); // 13 ms fibonacci(35); // 131 ms
  101. 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
  102. 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!
  103. 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
  104. Exponential Growth Runtime (ms) 0 4500 9000 13500 18000 Nth

    number of Fibonacci Sequence 0 10 20 30 40 50
  105. Recursive Call Tree Computing some of the same values several

    times! fibonacci(4) x 2 fibonacci(3) x 3
  106. Recursive Call Tree Computing some of the same values several

    times! fibonacci(4) x 2 fibonacci(3) x 3 fibonacci(2) x 5
  107. 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
  108. 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
  109. Fibonacci TCO function fibonacci(n, current = 0, next = 1)

    { if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); }
  110. Fibonacci TCO function fibonacci(n, current = 0, next = 1)

    { if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); } Two accumulators
  111. 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
  112. 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
  113. 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;
  114. 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
  115. 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
  116. 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
  117. 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
  118. 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
  119. 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
  120. 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
  121. Partial Application function partial(fn, ...args) { return (...otherArgs) => {

    return fn(...args, ...otherArgs); }; } Applied arguments
  122. Partial Application function partial(fn, ...args) { return (...otherArgs) => {

    return fn(...args, ...otherArgs); }; } Return new closure
  123. Partial Application function partial(fn, ...args) { return (...otherArgs) => {

    return fn(...args, ...otherArgs); }; } Remaining arguments
  124. Partial Application function partial(fn, ...args) { return (...otherArgs) => {

    return fn(...args, ...otherArgs); }; } Call original function with all arguments
  125. Partial Application: Why? • Build up functions from smaller pieces

    • Remove duplication • Generalize function abstraction (i.e. no additionFactory type functions)
  126. 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]
  127. 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?)
  128. 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
  129. 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
  130. 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
  131. 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
  132. 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
  133. 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
  134. Currying function curry(fn, len = fn.length) { return (...args) =>

    { if (args.length >= len) { return fn(...args); } return curry( partial(fn, ...args), len - args.length ); }; }
  135. 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!
  136. 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)
  137. 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
  138. 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
  139. 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
  140. 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
  141. 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]
  142. Function Composition • Compose functions together to form new functions

    • Pipe function output to next function • Helps enforce modularity/ SOC by keeping functions small
  143. 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
  144. 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
  145. 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
  146. 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)); }
  147. 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)); }
  148. 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)); }
  149. 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)); }
  150. 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)); }
  151. Why FP • Concise, elegant solutions to problems • Modularity

    and composition • Eliminate data races with immutability • Unit tests are simpler — no checking for state changes
  152. • 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)