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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. Iteration v Recursion

    View Slide

  8. Iteration
    Imperative
    Repetitive
    Stateful

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  12. Iteration v Recursion

    View Slide

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

    View Slide

  14. bu h o t
    efficiency?

    View 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 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 Slide

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

    View Slide

  18. in
    JavaScript
    we ur n

    View Slide

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

    View Slide

  20. bu h t ha
    happy ending

    View Slide

  21. JS

    View Slide

  22. Act I:
    Ite on, bu t i

    View Slide

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

    View Slide

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

    View Slide

  27. 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 Slide

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

    View Slide

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

    View Slide

  30. 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 Slide

  31. 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 Slide

  32. 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 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;
    }
    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 Slide

  34. 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 Slide

  35. 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 Slide

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

    View Slide

  37. Act II:
    Rec on, bu r ma

    View 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

    View 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 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 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 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 Slide

  41. 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 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 Slide

  43. 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 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));
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View 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

    View 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
    ^ s a k (li d e )

    View 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
    f a > recursiveInception(3)
    let dreams = recursiveInception(2);
    return dreams + " within a dream";

    View 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";
    )^

    View 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(0);
    return dreams + " within a dream";
    )^

    View 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 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 = recursiveInception(1);
    return dreams + " within a dream";
    recursiveInception(0)
    return "a dream";
    (
    ^

    View 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 = recursiveInception(1);
    return dreams + " within a dream";
    recursiveInception(1)
    let dreams = "a dream";
    return dreams + " within a dream";
    (
    ^

    View 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 = recursiveInception(2);
    return dreams + " within a dream";
    recursiveInception(2)
    let dreams = "a dream within a dream";
    return dreams + " within a dream";
    (
    ^

    View 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
    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 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 3
    a dream within a dream within a dream
    within a dream
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    View 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 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

    View 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";

    View 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";
    )^

    View 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";
    )^

    View 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
    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 Slide

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

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

    View Slide

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

    View 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) => {
    }
    Ta l-re s e

    View Slide

  67. 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 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 e p n ^
    to h e r o
    ^ ca t m a f
    Tail Recursion

    View 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
    Tail Recursion

    View 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
    us um or ^
    to “re b ” hi r
    in a c . ^ is ca
    Tail Recursion

    View 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
    in “ta si ”
    Tail Recursion

    View Slide

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

    View 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");

    View Slide

  75. 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 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 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(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 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

    View 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 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(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 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

    View 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 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(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 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 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 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 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);
    tailRecursiveInception(3)
    return "a dream within a dream within a dream within a dream";
    )^
    Ta l Op i z i

    View 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");
    }
    > tailRecursiveInception(3);
    a dream within a dream within a dream within a dream
    Ta l Op i z i

    View Slide

  88. 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 Slide

  89. 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 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
    T O L

    View 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)
    T O L

    View Slide

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

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

    View 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

    View Slide

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

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

    View Slide

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

    View Slide

  98. $ 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 Slide

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

    View Slide

  100. Iteration v Recursion

    View Slide

  101. JS
    Iteration Recursion

    View Slide

  102. 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 Slide

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

    View Slide