Using tail recursion instead of loops forces you to name the procedure, which is helpful documentation. Also, unlike a loop, you have to explicitly call back into it. For me, the edge cases are usually clearer with tail recursion and I find it harder to write infinite loops using tail recursion.
Regular non tail recursion can be fine in many cases so long as the recursion depth is limited. I have tested code that made at least 50000 recursive calls before blowing the stack. In many cases, you can just assume that won't happen or you can rewrite the function in a tail recursive way by adding additional parameters to the function with a slight loss of elegance to the code.
To cover all cases, I feel like there needs to be a cross-platform drop-in library to properly blow the stack in languages which feature tail call recursion. :)
Fixed-point combinators can be made to work with tail recursion, so I don't think tail recursion forces anyone to name anything. Certainly, how easy it is to go with this particular approach depends a lot on the semantics of your chosen (or imposed) programming language.
There are also languages where the standard guarantees code will use tail recursion in certain cases. An example is scheme (https://people.csail.mit.edu/jaffer/r5rs/Proper-tail-recursi...)
There also are languages where you can write recursive functions in a way that makes the compiler err out of it cannot make the calls in a tail-recursive way. An example is scala (https://www.scala-lang.org/api/2.12.1/scala/annotation/tailr...)
That removes the possibility that a programmer mistakenly believes the compiler will use tail recursion.
You youngun's have hidden behind APIs for so long you don't even know how things work.
tmtvl's comment at https://news.ycombinator.com/item?id=41983916 shows an algorithm that requires unbounded space.
Traversing a mutable binary tree can be done in fixed space but is not easier to do with generalized forms of recursion than with tail recursion or imperative iteration.
define walk-tree (tree fn &optional remaining-branches)
match tree
(Leaf l) ->
funcall fn l
if remaining-branches
walk-tree (first remaining-branches) fn (rest remaining-branches)
(Tree t r l) ->
funcall fn t
walk-tree r fn (push l remaining-branches)Recursion seems really clever if you live in a universe with clever coworkers maintaining your code a few years from now.