3 ms·
It depends on the language and the programming paradigm you're trying to approach, but I feel that TCO is important only in functional languages, and they don't
by Daishiman 17y ago
It depends on the language and the programming paradigm you're trying to approach, but I feel that TCO is important only in functional languages, and they don't matter much in in OO.
Haskell is a language that absolutely depends on TCO as the functional paradigm lends itself to recursion, as the preferred form is that there be no mutation of state within functions. Recursion is evidently the better form for traversing data structures based on recursively enumerable types.
Having said that, in practice very few OO languages use inmutability. The very purpose of OO is to model entities as objects and such objects provide interfaces for changing state, which is about as impure as it gets. Whether you feel it is better or worse than a functional approach, it is the modus operandum in OO languages, including Smalltalk, Ruby, Python, etc.
My own reality is that rarely do I work with recursive data structures at a productive level (not the case at the library level, where trees and lists are the norm), hence I find very little use for TCO. TCO is also very kludgy on arbitrary data structures with lots of twists (undirected graphs, meshes, etc.). Worse of all, it is very easy to get TCO wrong and actually having to use the stack.
The nice thing about implementing a function iteratively is that it is easy to see where state is being altered on every step. This is irrelevant in a language like Haskell which has a wonderful syntax for pattern matching which makes end cases and control flow very explicit, but it is decidedly very un-OO. Most pure OO languages, on the other hand, have ugly and expensive pattern matching (instanceOf and friends are pretty much what you have). I'm not convinced that TCO works very well in practice in the current OO languages. And if you're doing something that's altering state all the time it's very easy to structure a function such that it can be tail-called without having to preserve state.
In short, Ruby, Python, and Smalltalk don't have much place for TCO. TCO is not idiomatic, libraries are not made for it, and it is counterproductive to use TCO on languages that expect you to alter state all the time to model entities (unless you like recursing with 10-ary functions, which happens a lot on functional languages and it is not a pretty sight). Modern iterator patterns a pretty and work well, so it makes sense for languages that prioritize iterators over recursion to get one of them right when the other one hardly gets much use and can be easily done wrong. In my own experience (and it may not be yours) recursion is nice for 5-liner functions, but try to get a 50-line function to do TCO properly and you're probably in a world of hurt.
I do have to admit that, generally speaking, once you get the TCO to work more likely than not the function is correct and modern compilers can optimize them very aggresively.