> Even SBCL doesn't do TCO at all times. Compiling at (debug 3) means no TCO.
Presumably one intends to debug the code, when setting (debug 3). Then it'll be helpful to see the stack, no?
> Another related footgun is deep recursion of other kinds, for example when recursively traversing down lists. For long lists it's easy to exceed the stack size limit. The common idiom is to recur on list elements, but iterate or map to go along a list.
Not going to argue with seasoned lispers here, but IMHO recursive code makes most sense when accessing recursive data structures.
One place where this shows up is in parse trees. The grammar for a list of things may involve productions that look like list constructors. This, directly translated into a data structure, would give a very long chain of parse tree nodes dangling off to the right. It's a recursive data structure, but a very deep one for large lists, and traversing it recursively can use a lot of stack.
This can also be seen as an argument against building parse trees that way. Instead, have a node with an unbounded number of children, the elements of the list.
> Presumably one intends to debug the code, when setting (debug 3). Then it'll be helpful to see the stack, no?
You don't necessarily need to give up TCO to do that though. You just do some bookkeeping and synthesize virtual stack frames. DWARF has native facilities to handle this.
CL goes the route it does mostly out of history, which includes the fact it has its own debugging ecosystem, more than any fundamental technical reason. There are technical hurdles with doing this in an image-based dynamic compilation model, but it's very far from intractable. Especially if you just do what GHC did and add a DWARF workflow. Most CL users wouldn't ever touch it though, because that's a drastically different debugging model that costs them a lot of ergonomic power, which may even be the reason they're working in CL to begin with.