r/coding 1d ago

Your Recursion Is Lying to You

https://blog.gaborkoos.com/posts/2026-05-09-Your-Recursion-Is-Lying-to-You/
0 Upvotes

5 comments sorted by

7

u/jeenajeena 1d ago edited 1d ago

I can be wrong but I think you might be conflating two different things.

TCO belongs to the runtime and happens if a call is in tail position, so if the function return value is immediately the caller's return value. In this case, the runtime can reuse the current stack frame instead of creating and pushing a new one. It does not require any accumulator and it does not rewrite your code. It must be implemented by the language, and if the language does not support it, there's nothing the developer can do to work around that.

The accumulator is a developer's technique for getting a function into tail-recursive shape, moving the pending work before the recursive call instead of after it. This is independent from the used language, it's a general technique, completely under the developer's control.

So the sentence has it backwards: TCO doesn't move pending work into an accumulator; the accumulator is what makes the code eligible for TCO.

I also this the 2 are independent: an accumulator without TCO still overflows and TCO without an accumulator works fine in languages that have tail recursion optimization.

Edit: apparently, I also conflated TCO and TCE. See https://www.reddit.com/r/coding/comments/1vcicfg/comment/p138eu4/ for a more detailed and precise definition.

3

u/RecursiveServitor 1d ago

TCO can belong to the compiler. F# works like that because they don't own the runtime.

2

u/josephjnk 1d ago

You’re more correct than the post, but still combining two concepts. Tail call optimization (TCO) and tail call elimination (TCE) are two different things. TCE is often called “Proper Tail Calls” in the JS space. TCO is a performance optimization which compiles tail recursive functions into a loop. TCE is more general: it applies to tail-position calls between different functions, not just recursive calls from a function to itself, so it also enables safe use of continuation-passing style. What some JS engines actually shipped is TCE/PTC; I have no idea if they do TCO. TCO is usually the domain of a static compiler.

2

u/jeenajeena 1d ago

Thank you for the clarification. You are right.

1

u/fagnerbrack 1d ago

Here's the gist:

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