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. Functional Programming Basics in ES6 Jeremy Fairbank jeremyfairbank.com @elpapapollo

  2. Hi, I’m Jeremy jfairbank @elpapapollo pushagency.io blog.jeremyfairbank.com simplybuilt.com

  3. What is a function?

  4. What is a function?

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

    the domain with exactly one element in the range. Domain Range
  6. What is a function?

  7. What is a function?

  8. What is a function?

  9. What is a function? Domain

  10. What is a function? Domain Range

  11. Unique mapping from input to output! Domain Range

  12. Ugh, math?

  13. Alonzo Church

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

    computation in terms of “function abstraction and application using variable binding and substitution.”
  15. λ-Calculus Represent computation in terms of “function abstraction and application

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

    variable binding and substitution.” λ-Calculus
  17. Represent computation in terms of “function abstraction and application using

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

    variable binding and substitution.” λ-Calculus Substitution => apply arguments
  19. λ-Calculus Functions are anonymous.

  20. Functions are anonymous. λ-Calculus

  21. Functions are anonymous. λ-Calculus X

  22. Functions are anonymous. λ-Calculus Functions are expressions and units of

    computation.
  23. λ-Calculus Functions are single input.

  24. Functions are single input. λ-Calculus

  25. Functions are single input. λ-Calculus

  26. Functions are single input. λ-Calculus Break down into smaller pieces

    — currying!
  27. That’s nice. What about functional programming?

  28. λ-Calculus shows the power of functional computation. Functional programming brings

    it to life in computer science!
  29. What is Functional Programming?

  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?
  31. TL;DR Programming without assignment statements.

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

    • Referential Transparency and Purity • Recursion • Immutability • Partial Application and Currying • Composition
  33. Thar be ES6 ahead!

  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']
  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']
  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']
  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; };
  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); }
  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)); }
  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);
  41. Declarative vs. Imperative

  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
  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]
  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]
  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]
  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]
  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]
  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
  49. Declarative function doubleNumber(n) { return n * 2; } function

    doubleNumbers(numbers) { return numbers.map(doubleNumber); } doubleNumbers([1, 2, 3]); // [2, 4, 6]
  50. First Class Functions Functions are expressions/values.

  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); // ''
  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); // ''
  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); // ''
  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); // ''
  55. First Class Functions Functions as arguments

  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); }
  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); }
  58. First Class Functions Functions as return values

  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
  60. Closures

  61. Closures • Allow functions to reference other scopes • “Close

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

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

    (y) => x + y; } const add1 = additionFactory(1); add1(2); // 3 Closures Same x
  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'
  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'
  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
  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'
  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'
  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'
  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'
  71. Referential Transparency

  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
  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;
  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;
  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;
  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!
  77. Idempotency

  78. Idempotency • Special case of referential transparency • Multiple function

    applications produce the same result as one application
  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
  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
  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
  82. Purity

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

    No global/shared state • No impure arguments • No I/O
  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(' '); }
  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); }
  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
  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
  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
  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
  90. Recursion

  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
  92. Recursion: Factorial

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

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

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

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

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

  99. Recursion: Factorial factorial(4);

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

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

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

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

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

    factorial(2); 4 * 3 * 2 * factorial(1); 4 * 3 * 2 * 1; 4 * 3 * 2;
  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;
  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;
  107. What about recursion performance?

  108. Recursion Performance let value = factorial(100000); console.log(value); // ???

  109. Recursion Performance let value = factorial(100000); console.log(value); // ??? RangeError:

    Maximum call stack size exceeded
  110. Recursion: Factorial function factorial(n) { if (n < 2) {

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

    return 1; } return n * factorial(n - 1); }
  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;
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  123. Tail call optimization to the rescue!

  124. Tail Call Optimization

  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!)
  126. Tail Call Optimization Unoptimizable function factorial(n) { if (n <

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

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

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

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

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

    < 2) { return accum; } return factorial(n - 1, n * accum); } Current value accumulator
  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
  133. Optimize Factorial function factorial(n, accum = 1) { if (n

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

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

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

    < 2) { return accum; } return factorial(n - 1, n * accum); } 1 3 2
  137. And now? let value = factorial(100000); console.log(value); // Infinity

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

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

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

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

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

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

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

    factorial(2, 12); factorial(1, 24); 24;
  145. Recursion: Fibonacci

  146. Recursion: Fibonacci

  147. Recursion: Fibonacci Base cases

  148. Recursion: Fibonacci Recurrence

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

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

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

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

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

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

    fibonacci(20); // 0 ms
  156. Performance Revisited fibonacci(1); // 0 ms fibonacci(5); // 0 ms

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

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

    number of Fibonacci Sequence 0 10 20 30 40 50
  162. Recursive Call Tree

  163. Recursive Call Tree Computing some of the same values several

    times!
  164. Recursive Call Tree Computing some of the same values several

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

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

    times! fibonacci(4) x 2 fibonacci(3) x 3 fibonacci(2) x 5
  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
  168. TCO and Dynamic Programming

  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
  170. Fibonacci TCO function fibonacci(n, current = 0, next = 1)

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

    { if (n === 0) { return current; } return fibonacci(n - 1, next, current + next); } Two accumulators
  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
  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
  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;
  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
  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
  177. Partial Application

  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
  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
  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
  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
  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
  183. Partial Application function partial(fn, ...args) { return (...otherArgs) => {

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

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

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

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

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

    • Remove duplication • Generalize function abstraction (i.e. no additionFactory type functions)
  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]
  190. Currying

  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?)
  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
  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
  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
  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
  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
  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
  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 ); }; }
  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!
  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)
  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
  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
  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
  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
  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]
  206. Function Composition

  207. Function Composition

  208. Function Composition • Compose functions together to form new functions

    • Pipe function output to next function • Helps enforce modularity/ SOC by keeping functions small
  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
  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
  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
  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)); }
  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)); }
  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)); }
  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)); }
  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)); }
  217. </fp-basics>

  218. But why Functional Programming?

  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
  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)
  221. Thanks! jfairbank @elpapapollo blog.jeremyfairbank.com Code samples (some ES5 too) github.com/jfairbank/functional-javascript