ES6 Tail Call Optimization Explained
benignbemine.github.io
benignbemine.github.io
So close yet so far. This is JavaScript's curse.
That's not correct. Loops are just one kind (the simplest) of iteration. In a language with TCO, you can also implement state machines with function calls, or do continuation passing style (pass one or multiple functions to a function to be called with a result when done). There's a reason Guy Steele called the function the ultimate goto (assuming TCO): you can then build all control structures with them.
Missing TCO, you need to fall back on handling state yourself (like you can still implement recursive algorithms in a language that doesn't offer recursion, like old Basics or whatever, you just need to build your own stack).
I'm pretty sure Guy Steele's paper title was tonge-in-cheek, as around a decade before the well-known letter "Go To Statement Considered Harmful" by Edsger Dijkstra was published. How would one want to propagate something as awful as goto? Well, the new goto allowed to pass arguments with it, and would reinstate the context. This would make it safe from a memory corruption stand point, and also be cleaner for it's now all calling of mathematical functions. So you could do all that goto could, but in a clean way.
I don't know if Steele hat C in mind (which appeared just 5 years before the paper) or which other language(s). Also, perhaps "the ultimate goto" was a play on that it's even actually more powerful than goto in most languages? (I think I still haven't finished reading it, actually, and should, perhaps the answer is in the paper.)
If you want to use goto in C to have the full power of TCO, then you need to write your C program as a single C function, with all actual "functions" of your program represented by labels, and allocate a stack and pass arguments on that stack or in globals yourself. And can forget about any type checking and programmer sanity. (A saner way to archieve TCO in C will be to use trampolines. Or use a code generator instead of writing goto'ified C manually; the Gambit-C system uses this approach to compile Scheme to C.)
On the larger scheme (pun intended) of things, of course TCO is not essential, very few things are. However, the assertion "you can just always use a loop" is incorrect. To pull out just one example, with TCO I can have the producer-consumer coroutine for free without stack overflow. Loops are not the only use case for TCO.
Furthermore, there is nothing 'O' about it, doing extra work (maintaining call frames) not required by the semantics is not an optimization but an act of pessimization, unless there are compelling reasons to do that extra work. Guido feels lack of stacktraces is one such a compelling reason. I don't find that to be particularly compelling because that 'stack trace' is quite absent in the alternative he suggests: loops
This example of TCO adds to that.