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

CodeStock: Functional Programming Basics in JavaScript

CodeStock: Functional Programming Basics in JavaScript

Jeremy Fairbank

July 11, 2015
Tweet

More Decks by Jeremy Fairbank

Other Decks in Programming

Transcript

  1. Functional Programming Basics in JavaScript
    2:40 PM / Ballroom A

    View full-size slide

  2. Functional Programming Basics
    in JavaScript
    Jeremy Fairbank
    @elpapapollo

    View full-size slide

  3. Understand, apply, and create basic
    functional programming tenets and
    constructs in JavaScript.

    View full-size slide

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

    View full-size slide

  5. What is a function?

    View full-size slide

  6. What is a function?

    View full-size slide

  7. What is a function?
    Relation that pairs each
    element in the domain with
    exactly one element in the
    range.
    Domain Range

    View full-size slide

  8. What is a function?

    View full-size slide

  9. What is a function?

    View full-size slide

  10. What is a function?

    View full-size slide

  11. What is a function?
    Domain

    View full-size slide

  12. What is a function?
    Domain Range

    View full-size slide

  13. Unique mapping from input to output!
    Domain Range

    View full-size slide

  14. Alonzo Church

    View full-size slide

  15. Alonzo Church
    • Church-Turing Thesis
    • Lambda Calculus
    • Represent computation in
    terms of “function abstraction
    and application using variable
    binding and substitution.”

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  19. Represent computation in terms of “function abstraction and
    application using variable binding and substitution.”
    λ-Calculus
    Substitution => apply arguments

    View full-size slide

  20. λ-Calculus
    Functions are anonymous.

    View full-size slide

  21. Functions are anonymous.
    λ-Calculus

    View full-size slide

  22. Functions are anonymous.
    λ-Calculus
    X

    View full-size slide

  23. Functions are anonymous.
    λ-Calculus
    Functions are expressions and units of
    computation.

    View full-size slide

  24. λ-Calculus
    Functions are single input.

    View full-size slide

  25. Functions are single input.
    λ-Calculus

    View full-size slide

  26. Functions are single input.
    λ-Calculus

    View full-size slide

  27. Functions are single input.
    λ-Calculus
    Break down into smaller pieces — currying!

    View full-size slide

  28. That’s nice.
    What about functional
    programming?

    View full-size slide

  29. λ-Calculus shows the power of
    functional computation.
    Functional programming brings it to
    life in computer science!

    View full-size slide

  30. What is Functional
    Programming?

    View full-size slide

  31. – 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?

    View full-size slide

  32. TL;DR
    Programming without assignment statements.

    View full-size slide

  33. Key Concepts
    • Declarative (vs. Imperative)
    • First Class Functions
    • Referential Transparency and Purity
    • Recursion
    • Immutability
    • Partial Application and Currying
    • Composition

    View full-size slide

  34. Thar be ES2015 ahead!

    View full-size slide

  35. 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']

    View full-size slide

  36. 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']

    View full-size slide

  37. 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']

    View full-size slide

  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']
    var add = function(x, y) {
    return x + y;
    };
    var printAndReturn = function(string) {
    console.log(string);
    return string;
    };

    View full-size slide

  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 greet() {
    var g = arguments[0] === undefined ? 'Hi' : arguments[0];
    console.log(g);
    }

    View full-size slide

  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']
    function print(a) {
    var args = [].slice.call(arguments, 1);
    console.log.apply(console, [a],concat(args));
    }

    View full-size slide

  41. 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);

    View full-size slide

  42. Declarative vs. Imperative

    View full-size slide

  43. 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

    View full-size slide

  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]

    View full-size slide

  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]

    View full-size slide

  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]

    View full-size slide

  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]

    View full-size slide

  48. 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]

    View full-size slide

  49. 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

    View full-size slide

  50. Declarative
    function doubleNumber(n) {
    return n * 2;
    }
    function doubleNumbers(numbers) {
    return numbers.map(doubleNumber);
    }
    doubleNumbers([1, 2, 3]);
    // [2, 4, 6]

    View full-size slide

  51. First Class Functions
    Functions are expressions/values.

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  55. 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); // ''

    View full-size slide

  56. First Class Functions
    Functions as arguments

    View full-size slide

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

    View full-size slide

  58. 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);
    }

    View full-size slide

  59. First Class Functions
    Functions as return values

    View full-size slide

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

    View full-size slide

  61. 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

    View full-size slide

  62. 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

    View full-size slide

  63. Closures
    • Allow functions to reference other scopes
    • “Close over” variables in higher scopes
    • Critical to functional programming paradigms like partial
    application

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  66. 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'

    View full-size slide

  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'

    View full-size slide

  68. 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

    View full-size slide

  69. 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'

    View full-size slide

  70. 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'

    View full-size slide

  71. 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'

    View full-size slide

  72. 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'

    View full-size slide

  73. Referential Transparency

    View full-size slide

  74. 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

    View full-size slide

  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;

    View full-size slide

  76. 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;

    View full-size slide

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

    View full-size slide

  78. Referential
    Transparency
    let counter = 0;
    function addToCounter1(n) {
    counter++;
    return n + counter;
    }
    function addToCounter2(n) {
    let counter = 0;
    counter++;
    return n + counter;
    }
    // Not referentially transparent
    let value1 = addToCounter1(1) + addToCounter1(1); // 5
    let value2 = 2 + 3; // Not the same value every call!
    // Referentially transparent
    let otherValue1 = addToCounter2(1) + addToCounter2(1); // 4
    let otherValue2 = 2 + 2;

    View full-size slide

  79. Referential
    Transparency
    let counter = 0;
    function addToCounter1(n) {
    counter++;
    return n + counter;
    }
    function addToCounter2(n) {
    let counter = 0;
    counter++;
    return n + counter;
    }
    // Not referentially transparent
    let value1 = addToCounter1(1) + addToCounter1(1); // 5
    let value2 = 2 + 3; // Not the same value every call!
    // Referentially transparent
    let otherValue1 = addToCounter2(1) + addToCounter2(1); // 4
    let otherValue2 = 2 + 2;

    View full-size slide

  80. Referential
    Transparency
    let counter = 0;
    function addToCounter1(n) {
    counter++;
    return n + counter;
    }
    function addToCounter2(n) {
    let counter = 0;
    counter++;
    return n + counter;
    }
    // Not referentially transparent
    let value1 = addToCounter1(1) + addToCounter1(1); // 5
    let value2 = 2 + 3; // Not the same value every call!
    // Referentially transparent
    let otherValue1 = addToCounter2(1) + addToCounter2(1); // 4
    let otherValue2 = 2 + 2;

    View full-size slide

  81. Idempotency
    • Special case of referential transparency
    • Multiple function applications produce the same result as
    one application

    View full-size slide

  82. 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

    View full-size slide

  83. 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

    View full-size slide

  84. 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

    View full-size slide

  85. Purity
    • “Strict referential transparency”
    • No side effects
    • No global/shared state
    • No impure arguments
    • No I/O

    View full-size slide

  86. 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(' ');
    }

    View full-size slide

  87. 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(' ');
    }

    View full-size slide

  88. 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(' ');
    }

    View full-size slide

  89. 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(' ');
    }

    View full-size slide

  90. 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(' ');
    }
    • No side effects.
    • String and number
    arguments are immutable.

    View full-size slide

  91. 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);
    }

    View full-size slide

  92. 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

    View full-size slide

  93. 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

    View full-size slide

  94. 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

    View full-size slide

  95. 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

    View full-size slide

  96. 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

    View full-size slide

  97. Recursion: Factorial

    View full-size slide

  98. Recursion: Factorial
    function factorial(n) {
    let result = 1;
    while (n > 1) {
    result *= n--;
    }
    return result;
    }
    Imperative

    View full-size slide

  99. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  103. Recursion: Factorial

    View full-size slide

  104. Recursion: Factorial
    factorial(4);

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  110. 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;

    View full-size slide

  111. 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;

    View full-size slide

  112. What about recursion
    performance?

    View full-size slide

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

    View full-size slide

  114. Recursion Performance
    let value = factorial(100000);
    console.log(value); // ???
    RangeError: Maximum call
    stack size exceeded

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  117. 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;

    View full-size slide

  118. 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;

    Stack

    View full-size slide

  119. 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;

    factorial(4)
    Stack

    View full-size slide

  120. Recursion: Factorial

    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

    View full-size slide

  121. Recursion: Factorial

    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

    View full-size slide

  122. Recursion: Factorial

    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

    View full-size slide

  123. Recursion: Factorial

    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

    View full-size slide

  124. Recursion: Factorial

    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

    View full-size slide

  125. Recursion: Factorial

    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

    View full-size slide

  126. 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;
    Stack

    View full-size slide

  127. 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

    View full-size slide

  128. Tail call optimization to the
    rescue!

    View full-size slide

  129. Tail Call Optimization

    View full-size slide

  130. 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!)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  142. And now?
    let value = factorial(100000);
    console.log(value);
    // Infinity

    View full-size slide

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

    View full-size slide

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

    Stack

    View full-size slide

  145. Factorial TCO

    factorial(4, 1)
    Stack
    factorial(4 /*, 1 */);
    factorial(3, 4);
    factorial(2, 12);
    factorial(1, 24);
    24;

    View full-size slide

  146. Factorial TCO

    factorial(3, 4)
    Stack
    factorial(4 /*, 1 */);
    factorial(3, 4);
    factorial(2, 12);
    factorial(1, 24);
    24;

    View full-size slide

  147. Factorial TCO

    factorial(2, 12)
    Stack
    factorial(4 /*, 1 */);
    factorial(3, 4);
    factorial(2, 12);
    factorial(1, 24);
    24;

    View full-size slide

  148. Factorial TCO

    factorial(1, 24)
    Stack
    factorial(4 /*, 1 */);
    factorial(3, 4);
    factorial(2, 12);
    factorial(1, 24);
    24;

    View full-size slide

  149. Factorial TCO

    Stack
    factorial(4 /*, 1 */);
    factorial(3, 4);
    factorial(2, 12);
    factorial(1, 24);
    24;

    View full-size slide

  150. Recursion: Fibonacci

    View full-size slide

  151. Recursion: Fibonacci

    View full-size slide

  152. Recursion: Fibonacci
    Base
    cases

    View full-size slide

  153. Recursion: Fibonacci
    Recurrence

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  157. 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);
    }

    View full-size slide

  158. Performance Revisited
    fibonacci(1); // 0 ms

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  162. Performance Revisited
    fibonacci(1); // 0 ms
    fibonacci(5); // 0 ms
    fibonacci(20); // 0 ms
    fibonacci(30); // 13 ms
    fibonacci(35); // 131 ms

    View full-size slide

  163. 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

    View full-size slide

  164. 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!

    View full-size slide

  165. 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

    View full-size slide

  166. Exponential Growth
    Runtime (ms)
    0
    4500
    9000
    13500
    18000
    Nth number of Fibonacci Sequence
    0 10 20 30 40 50

    View full-size slide

  167. Recursive Call Tree

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  172. 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

    View full-size slide

  173. TCO and Dynamic Programming

    View full-size slide

  174. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  177. 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

    View full-size slide

  178. 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

    View full-size slide

  179. 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;

    View full-size slide

  180. 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

    View full-size slide

  181. 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

    View full-size slide

  182. Functional Arrays

    View full-size slide

  183. Functional Arrays
    • Common operations like map can be implemented in FP
    • Enforce immutability by returning new array
    • No looping; use recursion!

    View full-size slide

  184. Array#map
    Map values in array to other values in new array
    function map(fn, array) {
    if (array.length === 0) {
    return array;
    }
    const [head, ...tail] = array;
    return [fn(head), ...map(fn, tail)];
    }

    View full-size slide

  185. Array#map
    Map values in array to other values in new array
    function map(fn, array) {
    if (array.length === 0) {
    return array;
    }
    const [head, ...tail] = array;
    return [fn(head), ...map(fn, tail)];
    }
    Function first; good
    for currying

    View full-size slide

  186. Array#map
    Map values in array to other values in new array
    function map(fn, array) {
    if (array.length === 0) {
    return array;
    }
    const [head, ...tail] = array;
    return [fn(head), ...map(fn, tail)];
    }
    Grab the first element
    and remaining elements

    View full-size slide

  187. function map(fn, array) {
    if (array.length === 0) {
    return array;
    }
    const [head, ...tail] = array;
    return [fn(head), ...map(fn, tail)];
    }
    Array#map
    Map values in array to other values in new array
    var head = array[0];

    View full-size slide

  188. function map(fn, array) {
    if (array.length === 0) {
    return array;
    }
    const [head, ...tail] = array;
    return [fn(head), ...map(fn, tail)];
    }
    Array#map
    Map values in array to other values in new array
    var tail = array.slice(1);

    View full-size slide

  189. Array#map
    Map values in array to other values in new array
    function map(fn, array) {
    if (array.length === 0) {
    return array;
    }
    const [head, ...tail] = array;
    return [fn(head), ...map(fn, tail)];
    }
    Return new array with
    altered head and mapped
    array of remaining elements

    View full-size slide

  190. Array#map
    Map values in array to other values in new array
    function map(fn, array) {
    if (array.length === 0) {
    return array;
    }
    const [head, ...tail] = array;
    return [fn(head), ...map(fn, tail)];
    }
    return [fn(head)].concat(map(fn, tail));

    View full-size slide

  191. Array#map
    Map values in array to other values in new array
    function map(fn, array) {
    if (array.length === 0) {
    return array;
    }
    const [head, ...tail] = array;
    return [fn(head), ...map(fn, tail)];
    }
    Base case: empty array.

    View full-size slide

  192. Array#map
    Map values in array to other values in new array
    function square(n) {
    return n * n;
    }
    let numbers = [1, 2, 3];
    let squaredNumbers = map(square, numbers);
    // [1, 4, 9];

    View full-size slide

  193. Partial Application

    View full-size slide

  194. 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

    View full-size slide

  195. 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

    View full-size slide

  196. 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

    View full-size slide

  197. 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

    View full-size slide

  198. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  203. Partial Application
    function partial(fn, ...args) {
    return (...otherArgs) => {
    return fn(...args, ...otherArgs);
    };
    }
    Call original function
    with all arguments

    View full-size slide

  204. Partial Application: Why?
    • Build up functions from smaller pieces
    • Remove duplication
    • Generalize function abstraction (i.e. no
    additionFactory type functions)

    View full-size slide

  205. 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]

    View full-size slide

  206. 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?)

    View full-size slide

  207. 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

    View full-size slide

  208. 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

    View full-size slide

  209. 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

    View full-size slide

  210. 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

    View full-size slide

  211. 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

    View full-size slide

  212. 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

    View full-size slide

  213. Currying
    function curry(fn, len = fn.length) {
    return (...args) => {
    if (args.length >= len) {
    return fn(...args);
    }
    return curry(
    partial(fn, ...args),
    len - args.length
    );
    };
    }

    View full-size slide

  214. 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!

    View full-size slide

  215. 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)

    View full-size slide

  216. 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

    View full-size slide

  217. 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

    View full-size slide

  218. 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

    View full-size slide

  219. 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

    View full-size slide

  220. 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]

    View full-size slide

  221. Function Composition

    View full-size slide

  222. Function Composition

    View full-size slide

  223. Function Composition
    • Compose functions together
    to form new functions
    • Pipe function output to next
    function
    • Helps enforce modularity/
    SOC by keeping functions
    small

    View full-size slide

  224. 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

    View full-size slide

  225. 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

    View full-size slide

  226. 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

    View full-size slide

  227. 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));
    }

    View full-size slide

  228. 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));
    }

    View full-size slide

  229. 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));
    }

    View full-size slide

  230. 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));
    }

    View full-size slide

  231. 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));
    }

    View full-size slide

  232. But why Functional
    Programming?

    View full-size slide

  233. Why FP
    • Concise, elegant solutions to problems
    • Modularity and composition
    • Eliminate data races with immutability
    • Unit tests are simpler — no checking for state changes

    View full-size slide

  234. Extra Resources
    • Babel (babeljs.io)
    • Lodash (lodash.com)
    • Clojurescript (github.com/clojure/clojurescript)
    • Immutable.js (github.com/facebook/immutable-js)
    • React (facebook.github.io/react/)

    View full-size slide

  235. Thanks!
    jfairbank
    @elpapapollo
    blog.jeremyfairbank.com
    Code samples (some ES5 too)
    github.com/jfairbank/functional-javascript

    View full-size slide