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. so m u t e I ♥ The Recurse Center

    recurse.com outreachy.org
  2. 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
  3. 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 “ ”
  4. JS

  5. > 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
  6. 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
  7. 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
  8. 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
  9. 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 :/
  10. JS Ite on ES6: • Iterables via [Symbol.iterator] • Iterators

    via next() • Generators via function* and yield • for…of loops
  11. 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
  12. 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
  13. Iterables function iterableTreeNode(value) { let node = { value: value,

    left: undefined, right: undefined }; function recurseyIterator() { <output the value> // recursive case <if left, iterate over subtree> <if right, iterate over subtree> // 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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 )
  26. 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";
  27. 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"; )^
  28. 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"; )^
  29. 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"; )^
  30. 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"; ( ^
  31. 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"; ( ^
  32. 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"; ( ^
  33. 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"; )^
  34. 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
  35. 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
  36. 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
  37. 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";
  38. 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"; )^
  39. 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"; )^
  40. 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";
  41. 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"; :(
  42. 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
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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);
  52. 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");
  53. 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");
  54. 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"); )^
  55. 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"); ♻
  56. 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
  57. 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");
  58. 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"); ♻
  59. 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
  60. 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"; )^
  61. 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"; ♻
  62. 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
  63. 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
  64. 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
  65. 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
  66. 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
  67. 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
  68. // 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
  69. // 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
  70. // 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
  71. 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
  72. 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
  73. $ nvm use 6 Now using node v6.12.3 $ node

    --harmony_tailcalls tailRecursiveInception.js 30000 T O L
  74. $ 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
  75. 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
  76. Mulțum ! JSHeroes organizers & Ioana Chiorean Mapbox The Recurse

    Center community Max Rabkin @An a V ki ❤ an @ma x.co