In a language with proper tail-recursion, a call that is syntactically not surrounded by another call will not generate additional context at run-time, i.e. the call will not allocate place on the stack. This allows the use of recursive calls to
implement endless loops as they are common in any kind of event-processing system (e.g. a web-server, a GUI framework, or a component in a message driven system). The common way of implementing such loops in imperative languages is of course to use some variant of the while loop. However, using (tail-) recursion has a couple of advantages:

  • The code is more self-describing because it is possible to name the recursive function
  • The state required for the loop can be maintained in the arguments of the recursive function and is often manipulated purely functional instead of imperatively. E.g. if the state is a list, the recursive call may simply cons a new element on front of the list or drop the head of the list. If the state is a boolean flag, changing the value of the flag is as easy as doing the call with a new boolean value. No assignment is necessary at all.
  • It is possible to distribute the code across several, then mutually recursive, functions which makes it easier to read and understand

Of course any reasonable compiler will turn the recursive call back to a jump on the machine and generate assignments to registers or stack locations for the arguments but this is hidden from the programmer.