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

Recursion, Iteration, & JavaScript: A love story

Recursion, Iteration, & JavaScript: A love story

(presented at JS Heroes 2018: https://youtu.be/FmiQr4nfoPQ)

Recursion and iteration are two alternative paradigms for solving problems by breaking them down into smaller chunks; this talk is a deep dive into how these paradigms work, and how JS supports them both. There’s a fundamental tradeoff between the two: the stateful repetition of iteration is often more performant as it’s closer to the way our machines “think”, whereas many programmers find the self-referential abstraction of recursion easier to read & write. In JS prior to ES6, neither paradigm was especially easy to work with: iterative loops were laborious to read and write (not human-efficient), and recursion was limited by the fact that subsequent recursive calls to a function require additional frames added to the stack, potentially leading to stack overflow (not machine-efficient). Eventually, however, all three - recursion, iteration, and JS - were finally brought closer together. In this talk we’ll dive into the nature of the two paradigms, examine what an Iterable and a tail-recursive function are, and learn how Tail Call Optimization unites the two paradigms, allowing us to write code recursively and run it iteratively.

Anjana Sofia Vakil

April 20, 2018
Tweet

More Decks by Anjana Sofia Vakil

Other Decks in Programming

Transcript

  1. Recursion, Iteration, & JS:
    A lo t
    @AnjanaVakil ❤ JSHeroes 2018

    View full-size slide

  2. Bună! Mă c e ă
    @AnjanaVakil

    View full-size slide

  3. location data platform for developers
    maps, search, navigation
    web, iOS, Android, Unity...

    View full-size slide

  4. so m u t e I ♥
    The
    Recurse
    Center
    recurse.com
    outreachy.org

    View full-size slide

  5. I ♥
    programming paradigms
    le ’s e d ! youtu.be/Pg3UeB-5FdA

    View full-size slide

  6. t i s a f o
    star-crossed paradigms

    View full-size slide

  7. Iteration v Recursion

    View full-size slide

  8. Iteration
    Imperative
    Repetitive
    Stateful

    View full-size slide

  9. Iteration
    Imperative
    Repetitive
    Stateful
    © Columbia Pictures 1993 - image via IMDb

    View full-size slide

  10. Recursion
    Functional
    Self-referential
    Stateless
    © Warner Brothers 2010 - image via IMDb

    View full-size slide

  11. Recursion
    Functional
    Self-referential
    Stateless
    © Warner Brothers 2010 - image via IMDb

    View full-size slide

  12. Iteration v Recursion

    View full-size slide

  13. t o r i m , bo l e
    dignity

    View full-size slide

  14. bu h o t
    efficiency?

    View full-size slide

  15. Jon n B o
    “Pro m g Ae h s e n ro k in n e t s”
    youtu.be/JjDsP5n2kSM - h/t Yo h

    View full-size slide

  16. You might optimize for speed…
    You might optimize for space...
    But… there's a third thing you might want to
    optimize for, which is: years of your life
    required per program implementation.
    This is more important than those other things...
    or certainly equally important.
    Jon n B o
    “Pro m g Ae h s e n ro k in n e t s”
    youtu.be/JjDsP5n2kSM - h/t Yo h


    View full-size slide

  17. Machine
    Efficiency
    Time
    Space
    Human
    Efficiency
    Writing
    Reading
    Maintenance
    v

    View full-size slide

  18. in
    JavaScript
    we ur n

    View full-size slide

  19. yu , I’m u n
    Shakespeare
    © 20th Century Fox 1996 - image via IMDb

    View full-size slide

  20. bu h t ha
    happy ending

    View full-size slide

  21. Act I:
    Ite on, bu t i

    View full-size slide

  22. > var simpleTree = makeTree(simpleTreeNode);
    We l ♥t e
    function simpleTreeNode(value) {
    return {
    value: value,
    left: undefined,
    right: undefined
    };
    }
    function makeTree(nodeType) {
    var root = nodeType(0);
    root.left = nodeType(1);
    root.left.left = nodeType(2);
    root.right = nodeType(3);
    root.right.left = nodeType(4);
    root.right.right = nodeType(5);
    root.right.right.right = nodeType(6);
    return root;
    }
    0
    1
    2
    3
    4 5
    6

    View full-size slide

  23. Recursive tree traversal
    function recursiveTraverse(tree) {
    // base case
    if (!tree) return;
    // recursive case
    console.log(tree.value);
    recursiveTraverse(tree.left);
    recursiveTraverse(tree.right);
    }
    > recursiveTraverse(simpleTree);
    0
    1
    2
    3
    4
    5
    6

    View full-size slide

  24. Recursive tree traversal
    function recursiveTraverse(tree) {
    // base case
    if (!tree) return;
    // recursive case
    console.log(tree.value);
    recursiveTraverse(tree.left);
    recursiveTraverse(tree.right);
    }
    > recursiveTraverse(simpleTree);
    0
    1
    2
    3
    4
    5
    6
    in iv e d & w i !
    === hu -ef en

    View full-size slide

  25. Iterative tree traversal
    function iterativeTraverse(tree) {
    var todo = [tree];
    while (todo.length) {
    var next = todo.shift();
    if (next) {
    console.log(next.value);
    todo.unshift(next.left);
    todo.push(next.right);
    }
    }
    }
    > iterativeTraverse(simpleTree);
    0
    1
    2
    3
    4
    5
    6

    View full-size slide

  26. Iterative tree traversal
    function iterativeTraverse(tree) {
    var todo = [tree];
    while (todo.length) {
    var next = todo.shift();
    if (next) {
    console.log(next.value);
    todo.unshift(next.left);
    todo.push(next.right);
    }
    }
    }
    > iterativeTraverse(simpleTree);
    0
    1
    2
    3
    4
    5
    6
    ru j s e l,
    bu t i t i :/

    View full-size slide

  27. wo n’t o t u t
    “fo h a of t e…”

    View full-size slide

  28. JS
    Ite on ES6:
    ● Iterables via
    [Symbol.iterator]
    ● Iterators via next()
    ● Generators via
    function* and yield
    ● for…of loops

    View full-size slide

  29. Iterables
    function iterableTreeNode(value) {
    let node = {
    value: value,
    left: undefined,
    right: undefined
    };
    function recurseyIterator() {
    }
    node[Symbol.iterator]= recurseyIterator;
    return node;
    }
    function recurTraverse(tree) {
    if (!tree) return; // base case
    console.log(tree.value);
    // recursive case
    recurTraverse(tree.left);
    recurTraverse(tree.right);
    }
    + Recursion

    View full-size slide

  30. Iterables
    function iterableTreeNode(value) {
    let node = {
    value: value,
    left: undefined,
    right: undefined
    };
    function recurseyIterator() {
    }
    node[Symbol.iterator]= recurseyIterator;
    return node;
    }
    function recurTrav(tree) {
    console.log(tree.value);
    // recursive case
    if (tree.left) recurTrav(tree.left);
    if (tree.right) recurTrav(tree.right);
    // base case (no children): just return
    }
    + Recursion

    View full-size slide

  31. Iterables
    function iterableTreeNode(value) {
    let node = {
    value: value,
    left: undefined,
    right: undefined
    };
    function recurseyIterator() {

    // recursive case


    // base case: finish iteration
    }
    node[Symbol.iterator]= recurseyIterator;
    return node;
    }
    function recurTrav(tree) {
    console.log(tree.value);
    // recursive case
    if (tree.left) recurTrav(tree.left);
    if (tree.right) recurTrav(tree.right);
    // base case (no children): just return
    }
    + Recursion

    View full-size slide

  32. Iterables
    function iterableTreeNode(value) {
    let node = {
    value: value,
    left: undefined,
    right: undefined
    };
    function* recurseyIterator() {
    yield node.value;
    // recursive case
    if (node.left) yield* node.left;
    if (node.right) yield* node.right;
    // base case: finish iteration
    }
    node[Symbol.iterator]= recurseyIterator;
    return node;
    }
    function recurTrav(tree) {
    console.log(tree.value);
    // recursive case
    if (tree.left) recurTrav(tree.left);
    if (tree.right) recurTrav(tree.right);
    // base case (no children): just return
    }
    + Recursion

    View full-size slide

  33. Iterables
    function iterableTreeNode(value) {
    let node = {
    value: value,
    left: undefined,
    right: undefined
    };
    function* recurseyIterator() {
    yield node.value;
    // recursive case
    if (node.left) yield* node.left;
    if (node.right) yield* node.right;
    // base case: finish iteration
    }
    node[Symbol.iterator]= recurseyIterator;
    return node;
    }
    > let betterTree = makeTree(iterableTreeNode);
    > for (let val of betterTree) console.log(val);
    0
    1
    2
    3
    4
    5
    6

    View full-size slide

  34. JS
    It(er) ge b r:
    github.com/tc39/proposal-async-iteration
    tc39.github.io/ecma262
    ● Async Iterables via
    [Symbol.asyncIterator]
    ● AsyncIterators via
    next().then()
    ● Async Generators
    ● for await…of loops

    View full-size slide

  35. Iteration
    JavaScript
    Recursion
    =
    Human-efficient
    Iteration
    +
    +

    View full-size slide

  36. Act II:
    Rec on, bu r ma

    View full-size slide

  37. Recursion: the dream
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    a dream within a dream within a dream
    within a dream

    View full-size slide

  38. Recursion: the dream
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    a dream within a dream within a dream
    within a dream
    $ node recursiveInception.js 30
    a dream within a dream within a dream
    within a dream within a dream within a
    dream within a dream within a dream within
    a dream within a dream within a dream
    within a dream within a dream within a
    dream within a dream within a dream within
    a dream within a dream within a dream
    within a dream within a dream within a
    dream within a dream within a dream within
    a dream within a dream within a dream
    within a dream within a dream within a
    dream within a dream
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View full-size slide

  39. Recursion: the dream
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3000
    a dream within a dream within a dream
    within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a
    … you get the idea
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View full-size slide

  40. Recursion: the dream
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 30000
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View full-size slide

  41. Recursion: the reality
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 30000
    /home/anjana/JSHeroes/recursiveInception.js:4
    const recursiveInception = (n) => {
    ^
    RangeError: Maximum call stack size exceeded
    at recursiveInception (recursiveInception.js:4:28)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View full-size slide

  42. Recursion: the reality
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 30000
    /home/anjana/JSHeroes/recursiveInception.js:4
    const recursiveInception = (n) => {
    ^
    RangeError: Maximum call stack size exceeded
    at recursiveInception (recursiveInception.js:4:28)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    at recursiveInception (recursiveInception.js:6:16)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View full-size slide

  43. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View full-size slide

  44. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View full-size slide

  45. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ^ s a k (li d e )

    View full-size slide

  46. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    f a > recursiveInception(3)
    let dreams = recursiveInception(2);
    return dreams + " within a dream";

    View full-size slide

  47. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(3)
    let dreams = recursiveInception(2);
    return dreams + " within a dream";
    recursiveInception(2)
    let dreams = recursiveInception(1);
    return dreams + " within a dream";
    )^

    View full-size slide

  48. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(3)
    let dreams = recursiveInception(2);
    return dreams + " within a dream";
    recursiveInception(2)
    let dreams = recursiveInception(1);
    return dreams + " within a dream";
    recursiveInception(1)
    let dreams = recursiveInception(0);
    return dreams + " within a dream";
    )^

    View full-size slide

  49. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(3)
    let dreams = recursiveInception(2);
    return dreams + " within a dream";
    recursiveInception(2)
    let dreams = recursiveInception(1);
    return dreams + " within a dream";
    recursiveInception(1)
    let dreams = recursiveInception(1);
    return dreams + " within a dream";
    recursiveInception(0)
    return "a dream";
    )^

    View full-size slide

  50. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(3)
    let dreams = recursiveInception(2);
    return dreams + " within a dream";
    recursiveInception(2)
    let dreams = recursiveInception(1);
    return dreams + " within a dream";
    recursiveInception(1)
    let dreams = recursiveInception(1);
    return dreams + " within a dream";
    recursiveInception(0)
    return "a dream";
    (
    ^

    View full-size slide

  51. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(3)
    let dreams = recursiveInception(2);
    return dreams + " within a dream";
    recursiveInception(2)
    let dreams = recursiveInception(1);
    return dreams + " within a dream";
    recursiveInception(1)
    let dreams = "a dream";
    return dreams + " within a dream";
    (
    ^

    View full-size slide

  52. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(3)
    let dreams = recursiveInception(2);
    return dreams + " within a dream";
    recursiveInception(2)
    let dreams = "a dream within a dream";
    return dreams + " within a dream";
    (
    ^

    View full-size slide

  53. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(3)
    let dreams = "a dream within a dream within a dream"
    ;
    return dreams + " within a dream";
    )^

    View full-size slide

  54. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 3
    a dream within a dream within a dream
    within a dream
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View full-size slide

  55. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 30000
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View full-size slide

  56. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 30000
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View full-size slide

  57. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 30000
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(30000)
    let dreams=recursiveInception(29999);
    return dreams + " within a dream";

    View full-size slide

  58. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 30000
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(30000)
    let dreams=recursiveInception(29999);
    return dreams + " within a dream";
    recursiveInception(29999)
    let dreams=recursiveInception(29998);
    return dreams + " within a dream";
    )^

    View full-size slide

  59. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 30000
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(30000)
    let dreams=recursiveInception(29999);
    return dreams + " within a dream";
    recursiveInception(29999)
    let dreams=recursiveInception(29998);
    return dreams + " within a dream";
    recursiveInception(29998)
    let dreams=recursiveInception(29997);
    return dreams + " within a dream";
    )^

    View full-size slide

  60. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 30000
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(30000)
    let dreams=recursiveInception(29999);
    return dreams + " within a dream";
    recursiveInception(29999)
    let dreams=recursiveInception(29998);
    return dreams + " within a dream";
    recursiveInception(29998)
    let dreams=recursiveInception(29997);
    return dreams + " within a dream";
    )^
    recursiveInception(29997)
    let dreams=recursiveInception(29996);
    return dreams + " within a dream";

    View full-size slide

  61. Recursion ca s k
    // recursiveInception.js
    'use strict';
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    const depth = parseInt(process.argv[2]);
    console.log(recursiveInception(depth));
    $ node recursiveInception.js 30000
    RangeError: Maximum call stack size exceeded
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    recursiveInception(30000)
    let dreams=recursiveInception(29999);
    return dreams + " within a dream";
    recursiveInception(29999)
    let dreams=recursiveInception(29998);
    return dreams + " within a dream";
    recursiveInception(29998)
    let dreams=recursiveInception(29997);
    return dreams + " within a dream";
    recursiveInception(29997)
    let dreams=recursiveInception(29996);
    return dreams + " within a dream";
    :(

    View full-size slide

  62. wo n’t o to r e
    wi t a n t f a s?

    View full-size slide

  63. JS
    Ta l Op i z i
    ● Implementation feature of
    (some*) JS engines
    ● Lets us ♻ stack frames:
    don’t need to “remember”
    anything from calling context
    ● Requires call in tail position:
    “proper tail calls” in ES6

    View full-size slide

  64. Tail Recursion
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    f o s e w ^
    af r u s al
    Not -re s e

    View full-size slide

  65. Tail Recursion
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    f o s e w ^
    af r u s al
    Not -re s e
    const tailRecursiveInception = (n) => {
    }
    Ta l-re s e

    View full-size slide

  66. Tail Recursion
    const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    f o s e w ^
    af r u s al
    Not -re s e
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    Ta l-re s e
    us e p n ^
    to h e r o

    View full-size slide

  67. const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    f o s e w ^
    af r u s al
    Not -re s e
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    Ta l-re s e
    us e p n ^
    to h e r o
    ^ ca t m a f
    Tail Recursion

    View full-size slide

  68. const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    f o s e w ^
    af r u s al
    Not -re s e
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    Ta l-re s e
    us um or ^
    to “re b ” hi r
    Tail Recursion

    View full-size slide

  69. const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    f o s e w ^
    af r u s al
    Not -re s e
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    Ta l-re s e
    us um or ^
    to “re b ” hi r
    in a c . ^ is ca
    Tail Recursion

    View full-size slide

  70. const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    f o s e w ^
    af r u s al
    Not -re s e
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    Ta l-re s e
    ^ re s e l
    in “ta si ”
    Tail Recursion

    View full-size slide

  71. const recursiveInception = (n) => {
    if (n === 0) return "a dream";
    let dreams = recursiveInception(n-1);
    return dreams + " within a dream";
    }
    f o s e w ^
    af r u s al
    Not -re s e
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    Ta l-re s e
    ^ re s e l
    la t g f o s
    Tail Recursion

    View full-size slide

  72. Ta l Op i z i
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);

    View full-size slide

  73. Ta l Op i z i
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");

    View full-size slide

  74. Ta l Op i z i
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");
    )^
    incept(3, "a dream")
    return incept(2, "a dream within a dream");

    View full-size slide

  75. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");
    incept(3, "a dream")
    return incept(2, "a dream within a dream");
    Ta l Op i z i
    incept(2, "a dream within a dream")
    return incept(1, "a… w… within a dream");
    )^

    View full-size slide

  76. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");
    incept(3, "a dream")
    return incept(2, "a dream within a dream");
    Ta l Op i z i
    incept(2, "a dream within a dream")
    return incept(1, "a… w… within a dream");

    View full-size slide

  77. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");
    incept(2, "a dream within a dream")
    return incept(1, "a… w… within a dream");
    Ta l Op i z i

    View full-size slide

  78. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");
    incept(2, "a dream within a dream")
    return incept(1, "a… w… within a dream");
    Ta l Op i z i
    )^
    incept(1, "a dream within a dream within a dream")
    return incept(0, "a… w… w… within a dream");

    View full-size slide

  79. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");
    incept(2, "a dream within a dream")
    return incept(1, "a… w… within a dream");
    Ta l Op i z i
    incept(1, "a dream within a dream within a dream")
    return incept(0, "a… w… w… within a dream");

    View full-size slide

  80. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");
    incept(1, "a dream within a dream within a dream")
    return incept(0, "a… w… w… within a dream");
    Ta l Op i z i

    View full-size slide

  81. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");
    incept(1, "a dream within a dream within a dream")
    return incept(0, "a… w… w… within a dream");
    Ta l Op i z i
    incept(0, "a dream within a dream within a dream within a dream")
    return "a dream within a dream within a dream within a dream";
    )^

    View full-size slide

  82. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");
    incept(1, "a dream within a dream within a dream")
    return incept(0, "a… w… w… within a dream");
    Ta l Op i z i
    incept(0, "a dream within a dream within a dream within a dream")
    return "a dream within a dream within a dream within a dream";

    View full-size slide

  83. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");
    incept(0, "a dream within a dream within a dream within a dream")
    return "a dream within a dream within a dream within a dream";
    Ta l Op i z i

    View full-size slide

  84. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return incept(3, "a dream");
    incept(0, "a dream within a dream within a dream within a dream")
    return "a dream within a dream within a dream within a dream";
    (
    ^
    Ta l Op i z i

    View full-size slide

  85. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    tailRecursiveInception(3)
    return "a dream within a dream within a dream within a dream";
    )^
    Ta l Op i z i

    View full-size slide

  86. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    > tailRecursiveInception(3);
    a dream within a dream within a dream within a dream
    Ta l Op i z i

    View full-size slide

  87. const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    console.log(tailRecursiveInception(3));
    let n = 3;
    let dreams = "a dream";
    while (n > 0) {
    dreams += " within a dream";
    n--;
    }
    console.log(dreams);
    Recursion + T O ≈ Iteration
    ^ w i t ke s ^ ru s li h

    View full-size slide

  88. T O L
    // tailRecursiveInception.js
    'use strict';
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    const depth = parseInt(process.argv[2]);
    console.log(tailRecursiveInception(depth));
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    View full-size slide

  89. // tailRecursiveInception.js
    'use strict';
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    const depth = parseInt(process.argv[2]);
    console.log(tailRecursiveInception(depth));
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $ node tailRecursiveInception.js 30000
    T O L

    View full-size slide

  90. // tailRecursiveInception.js
    'use strict';
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    const depth = parseInt(process.argv[2]);
    console.log(tailRecursiveInception(depth));
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $ node tailRecursiveInception.js 30000
    /home/anjana/JSHeroes/tailRecursiveInception.js:5
    const incept = (n, dreams) => {
    ^
    RangeError: Maximum call stack size exceeded
    at incept (tailRecursiveInception.js:5:18)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    T O L

    View full-size slide

  91. // tailRecursiveInception.js
    'use strict';
    const tailRecursiveInception = (n) => {
    const incept = (n, dreams) => {
    if (n === 0) return dreams;
    return incept(n-1, dreams+" within a dream");
    }
    return incept(n, "a dream");
    }
    const depth = parseInt(process.argv[2]);
    console.log(tailRecursiveInception(depth));
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $ node tailRecursiveInception.js 30000
    /home/anjana/JSHeroes/tailRecursiveInception.js:5
    const incept = (n, dreams) => {
    ^
    RangeError: Maximum call stack size exceeded
    at incept (tailRecursiveInception.js:5:18)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    at incept (tailRecursiveInception.js:7:12)
    li ! be y !
    T O L

    View full-size slide

  92. Ta l Op i z i
    Feature of (some*) JS engines

    View full-size slide

  93. Ta l Op i z i
    Feature of (some*) JS engines
    * On a c e; mo do im m T O
    kangax.github.io/compat-table/es6/#test-proper_tail_calls_(tail_call_optimisation)
    ● Webkit/Safari has it: webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/
    ● V8/Chromium/Node had it (w/ flag), removed it: bugs.chromium.org/p/v8/issues/detail?id=4698

    View full-size slide

  94. Ta l Op i z i
    Feature of (some*) JS engines
    * On a c e; mo do im m T O
    kangax.github.io/compat-table/es6/#test-proper_tail_calls_(tail_call_optimisation)
    ● Webkit/Safari has it: webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/
    ● V8/Chromium/Node had it (w/ flag), removed it: bugs.chromium.org/p/v8/issues/detail?id=4698
    Hot d ed ur ; di s o s o n !
    ● TCO issues explained in V8 blog: v8project.blogspot.ro/2016/04/es6-es7-and-beyond.html
    ● TC39 proposal for explicit tail call syntax (inactive): github.com/tc39/proposal-ptc-syntax
    ● Ongoing discussion/proposal for WebAssembly: github.com/WebAssembly/tail-call

    View full-size slide

  95. $ nvm use 6
    Now using node v6.12.3
    T O L

    View full-size slide

  96. $ nvm use 6
    Now using node v6.12.3
    $ node --harmony_tailcalls tailRecursiveInception.js 30000
    T O L

    View full-size slide

  97. $ nvm use 6
    Now using node v6.12.3
    $ node --harmony_tailcalls tailRecursiveInception.js 30000
    a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a
    dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within
    a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream
    within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within …………………..
    T O L

    View full-size slide

  98. Recursion
    JavaScript
    Iteration
    =
    Machine-efficient
    Recursion
    +
    +

    View full-size slide

  99. Iteration v Recursion

    View full-size slide

  100. JS
    Iteration Recursion

    View full-size slide

  101. Fur r a n :
    Exploring JS
    b D . Axe R c ay
    exploringjs.com
    ● ES6 - Iteration: Ch. 17, 21, 22; TCO: Ch. 27
    ● ES2018-ES2019: Asynchronous Iteration
    MDN
    developer.mozilla.org
    ● JavaScript>Reference>Iteration_protocols
    ● JavaScript>Guide>Functions>Recursion

    View full-size slide

  102. Mulțum !
    JSHeroes organizers & Ioana Chiorean
    Mapbox
    The Recurse Center community
    Max Rabkin
    @An a V ki ❤ an @ma x.co

    View full-size slide