Comment by LegionMammal978
1 day ago
> Tail-recursion and iteration are broadly equivalent right? So picking one over the other is a matter of style and capability.
Tail recursion is more closely related to goto statements. So one could argue that imperative iteration constructs, as with other forms of structured programming, are superior in giving the reader immediate knowledge of the broad control flow, while tail recursion (especially mutual tail recursion) could have all sorts of weird behaviors.
> while tail recursion (especially mutual tail recursion) could have all sorts of weird behaviors.
What "weird behaviors" have you encountered with tail recursion?
Well, by broadly equivalent, I mean that it's generally easy to translate from one form to another; not that they exactly match. Iteration isn't really capable of expressing all the things you can express with mutual tail calls, but otherwise.
You can certainly argue about which of two forms is clearer, but I don't know that it's a decidable problem, so like ... do whatever you feel. I write in multiple programming languages and try to blend with the local idioms, so I write loops with foreach and with recursion, and they're both fine. If you have pattern matched function clauses, I don't find recursion to be lacking in indications of the broad control flow; if you're in a language that lacks that, then recursive loops look pretty ugly. But something like a loop to sum values would be (in Erlang)
Assuming you know the syntax and the idioms, this is very clear. If you don't know the idioms, Acc means 'accumulator', H means 'head', T means 'tail', so if you call sum(List), it calls into the recursive sum with an initial accumulator of 0, then one element at a time is taken off the front of the list, and added to the accumulator, when the list is empty, the accumulator is returned.
the same thing in Rust would look like
You could make it generic or whatever if you want. Is one form better at giving the reader immediate knowledge of the broad control flow than the other? I don't know. But the iterative version is 7 lines vs 4 lines in recursion. A list isn't really the same as an array slice of course, so that's a bit of a cheat. Using a tuple in Erlang is similar, but wordier:
And you have to know that Tuples are 1 indexed, and that N indicates the Nth element of a Tuple. You could make sum/1 take a list or a tuple, but you do have two write both versions... you could make the Rust take an IntoIterator and benefit from Traits to not have to write a different version for each kind of container. You could argue about what's a better choice, but I won't :) Maybe you like type annotations, you could add those to the Erlang, but I won't do that either :P
If your iteration has more exciting work than summation, the line count difference doesn't matter so much... maybe the work it shorter in one language than the other anyway. But I don't think either form is obviously and objectively superior. I'd wager iteration has more mind share, but that doesn't indicate that it's superior.