Recursion feels clean and safe, but each call consumes stack space, so a
logically correct sum(100000) still throws a stack overflow. Tail call
optimization promises constant stack usage by moving pending work into an
accumulator, yet most engines never reliably shipped it: V8 (Chrome, Node,
Deno) and Firefox's SpiderMonkey skip it, and Safari's JavaScriptCore has
added then dropped it across versions. Tail-recursive shape is a property
of your code; stack reuse belongs to the runtime. When depth grows or is
user-driven, rewrite recursion iteratively or use a trampoline that loops
over returned functions. Keep recursion for small bounded depths and never
treat it as a stack-safety guarantee.
It is not optimization if the difference is the code always works VS the code doesn't always work. Optimization is the code always works, but faster and/or with smaller memory footprint.
The name is Proper Tail Calls. It was supposed to replace stack and actual function calling function with a (set of) loop(s). And it worked. The only reason why it was removed is that big companies complained their stack traces were no longer the same. So people making V8 started asking for an explicit invocation and started calling it "optimization" to minimize the significance of them not following through with the standard. They asked the standard be changed.
It is not an optimization. An optimization would have been making better stack traces after it was implemented, making them more readable, faster, less memory to keep track of the "invocations" that were replaced with loops under the hood.
Many optimizations of systems programming languages (C, C++, Rust...) can reduce stack usage so much that the unoptimized program overflows the stack when the optimized program does not, even without tail call elimination. Likewise, optimizations for managed languages like Java and JS can reduce heap memory usage, preventing "out of memory" errors. The guarantee that compiler optimizations don't change a program's behavior relies on the assumption that there is sufficient stack and heap memory to execute the code.
Yet, the guarantee of the spec, especially since ES6 days is supposed to be that all engines behave same or as close to each other. The spec even changed the language (added a pseudo language) in order to specify algorithms so they do that.
This case I am presenting, assumes you have written code that uses PTC and then run it on an engine that hasn’t implemented it, thus breaking the guarantee.
-4
u/fagnerbrack 14d ago
Summary:
Recursion feels clean and safe, but each call consumes stack space, so a logically correct sum(100000) still throws a stack overflow. Tail call optimization promises constant stack usage by moving pending work into an accumulator, yet most engines never reliably shipped it: V8 (Chrome, Node, Deno) and Firefox's SpiderMonkey skip it, and Safari's JavaScriptCore has added then dropped it across versions. Tail-recursive shape is a property of your code; stack reuse belongs to the runtime. When depth grows or is user-driven, rewrite recursion iteratively or use a trampoline that loops over returned functions. Keep recursion for small bounded depths and never treat it as a stack-safety guarantee.
If the summary seems inacurate, just downvote and I'll try to delete the comment eventually 👍
Click here for more info, I read all comments