| >Any recursive function can be transformed into a tail recursive form, exchanging stack allocation for heap allocation. And any tail recursive program can be transformed into a loop (a trampoline). It's really not the language construct that is the issue here. Not every recursion can be transformed into tail recursion. If it's not simply tail recursion (which is the high level language compiler way of implementing the old tried and true assembly language optimization technique of making the last function call be a JMP instead of a JSR followed by an RTS, that I learned from WOZ's Apple ][ 6502 monitor ROM code), you still have to manage the state somewhere. There ain't no such thing as a free lunch. And if not on the C stack, then it has to be somewhere else, like another stack or linked list, and then you still have to apply your own limits to keep it from being unlimited, so you're back where you started (but in a Sisyphusly tail call recursive way, you still have to solve the problem again). https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule >Greenspun's tenth rule of programming "Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp." Of course you can always just pass a recursion depth limit parameter down the recursion and stop when it expires, even letting the caller decide how deep a recursion they want to permit, which is easier and safer than using malloc or cons or std::vector to roll your own stack. Or the compiler could even do that for you, but you still have to figure out how to gracefully bottom out... So if "Exception Handling" is hopefully failing upward, then "Depthception Handling" is hopelessly failing downward! Depthception Handling is the vertical reflection of Exception Handling, with "implicit depth" recursion depth passing, like C++'s "implicit this" but deeper, not to be confused with the orthogonal "continuation passing" / "halt passing" dichotomy (passing HALT and CATCHFIRE instructions around but promising to never call them except in fatal emergencies): "try" / "catch" = An active attempt to solve things. "giveup" / "bottom" = Passive resignation to inevitability. "throw" is an emergency exit UP. "drop" is an emergency slide DOWN. "try" => "giveup" introduces a futile attempt at unbounded recursion. "catch" => "bottom" declares a bottoming out handler after a function signature, to guard its body below. "throw" => "drop" immediately bottoms out from any depth! fn doomed_recursion()
bottom { println!("Too deep, I give up."); }
{
giveup {
if going_too_deep() {
drop;
}
doomed_recursion();
}
}
And you can even bind the depth for explicit depth monitoring and control: fn deep_trouble(depth: Depth)
bottom { println!("I reached depth {} and surrendered.", depth); }
{
giveup depth {
if depth > 10 {
drop;
}
deep_trouble(depth++);
}
}
|
Ok, every recursion in a language that supports programmer-managed state can be transformed into a tail recursion. That still means any recursion in all serious programming languages and most joke programming languages too.