← Back to context

Comment by artemonster

7 hours ago

Can someone give some real world examples for TCO? Every time I see this I only see fibonacci and gcd and I really want to encounter this one in the wild on something real and applicable

State machines come to mind: a transition is just a function call. Unfortunately that's a general tail call, not always a recursive one, so no love from this library, and that's where "proper" TCO wins (or trampolines if $your_language lacks TCO)

Also it wouldn't help with Fibonacci, since while it's recursive, it's not tail-recursive (yes, it can be written that way, but I'm talking about the idiomatic naive definition).

I don't know about real world "examples", but the beauty of tail-call recursion specifically is the theoretical insight that they have a one-to-one mapping with an loop-based equivalent formulation, and vice versa (which is generally not necessarily true of all recursion).

But, for languages that don't have loop constructs and you need to rely on recursion, all you need to do is write your recipe in standard loop form, and then map back to a tail-call syntax. This is often a LOT easier than trying to think of the problem in a recursive mindset from scratch. (though occasionally, the reverse is also true.)

So the only constraint for re-implementing such looped logic onto tailcalls is that this relies on the stack, which may overflow. By providing TCO you are effectively removing that restriction, so it's a very useful thing for a language to support (especially if they don't provide low-level loops).

The title "tail call optimisation" in the package above is a bit of a misnomer, since this is more of a "transformation" than an "optimisation", but effectively the whole loop-tailcall equivalence is exactly what the package mentioned above relies on to work; it uses decorators to transform tail-call recursive functions to their equivalent loop-based formulations, and thus passing the need to create multiple stacks for the recursion (and risk stack overflow), since the translated loop will now take place in a single stack frame.

  • I know what TCO is. Screw the "beauty", honestly. I want to see at least one real world use case

    • There isn't a killer use case, because tail calls (to yourself or to siblings) can always be easily converted to a loop, and the loop is more idiomatic in most mainstream languages.

      1 reply →

Lua has mandatory TCO and several games I've been on which use it for a scripting use TCO for a state machine. Easy to debug - just look at the stack!

For tco to be really useful you need to think in a non procedural way. Imagine that you don't have loops in your language so you need recursion to do stuff multiple times.

Also even in procedural languages there are some problems that are easier to understand and model if you use recursion, for example tree or graph like structures.

  • traversing graph or a tree is not a TCO case because it would involve a stack/queue for DFS/BFS, whatever. I dont want to think in non procedural way, I reserve this nonsense to haskellers, please provide me a valid python use case for TCO :)

It gives you arbitrarily complex control flow even in presence of modularity. A tail call is a state transition. Without them, you'd have to resort to a big loop (which breaks modularity), or some sort of trampoline (which works but it's a bit clumsy).

A really simple one is traversing a linked list (or any naturally recursive data structure, such as a dictionary or tree). It is very natural to traverse a recursive data structure recursively.