[Python-ideas] Tail recursion elimination

Steven D'Aprano steve at pearwood.info
Sun Jan 19 01:45:16 CET 2014


On Sat, Jan 18, 2014 at 10:29:46AM -0800, Andrew Barnert wrote:

> Whether or not you really need it, adding it to Python is a fun 
> challenge that's worth trying.

"Need" is a funny thing. There's nothing you can do with a for-loop that 
you can't do with a while-loop, but that doesn't mean we don't "need" 
for-loops. Certain algorithms and idioms are simply better written in 
terms of for-loops, and certain algorithms are simply better written in 
terms of recursion than looping.

You can go a long way without recursion, or only shallow recursion. In 
15 years + of writing Python code, I've never been in a position that I 
couldn't solve a problem because of the lack of tail recursion. But 
every time I manually convert a recursive algorithm to an iterative one, 
I feel that I'm doing make-work, manually doing something which the 
compiler is much better at than I am, and the result is often less 
natural, or even awkward. (Trampolines? Ewww.)


> Third, eliminating tail calls means the aren't on the stack at 
> runtime, which means there's no obvious way to display useful 
> tracebacks. I don't think too many Python users would accept the 
> tradeoff of giving up good tracebacks to enable certain kinds of 
> non-pythonic code, 

What makes you say that it is "non-pythonic"? You seem to be assuming 
that *by definition* anything written recursively is non-pythonic. I do 
not subscribe to that view.

In fact, in some cases, I *would* willingly give up *non-useful* 
tracebacks for the ability to write more idiomatic code. Have you seen 
the typical recursive traceback? They're a great argument for "less is 
more". What normally happens is that you get a RuntimeError and the 
traceback blows away your xterm's buffer with hundreds of identical or 
near-identical lines. But even in the case where you didn't hit the 
recursion limit, the traceback is pretty much a picture of redundancy 
and noise:


py> a(7)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./rectest.py", line 2, in a
    return b(n-1)
  File "./rectest.py", line 5, in b
    return c(n-1) + a(n)
  File "./rectest.py", line 2, in a
    return b(n-1)
  File "./rectest.py", line 5, in b
    return c(n-1) + a(n)
  File "./rectest.py", line 2, in a
    return b(n-1)
  File "./rectest.py", line 5, in b
    return c(n-1) + a(n)
  File "./rectest.py", line 2, in a
    return b(n-1)
  File "./rectest.py", line 5, in b
    return c(n-1) + a(n)
  File "./rectest.py", line 2, in a
    return b(n-1)
  File "./rectest.py", line 5, in b
    return c(n-1) + a(n)
  File "./rectest.py", line 2, in a
    return b(n-1)
  File "./rectest.py", line 5, in b
    return c(n-1) + a(n)
  File "./rectest.py", line 9, in c
    return 1/n
ZeroDivisionError: division by zero


The only thing that I care about is the very last line, that function c 
tries to divide by zero. The rest of the traceback is just noise, I 
don't even look at it.

Now, it's okay if you disagree, or if you can see something useful in 
the traceback other than the last entry. I'm not suggesting that TCE 
should be compulsary. I would be happy with a commandline switch to 
turn it on, or better still, a decorator to apply it to certain 
functions and not others. I expect that I'd have TCE turned off for 
debugging. But perhaps not -- it's not like Haskell and Scheme 
programmers are unable to debug their recursive code.

The point is that tracebacks are not sacrosanct, and, yes, I would like 
the choice between writing idiomatic recursive code and more extensive 
tracebacks. Trading off speed for convenience is perfectly Pythonic -- 
that's why we have the ability to write C extensions, is it not?


> but even if you don't solve this, you can always 
> maintain a fork the same way that Stackless has been doing.

Having to fork the entire compiler just to write a few functions in 
their most idiomatic, natural (recursive) form seems a bit extreme, 
wouldn't you say? 



-- 
Steven


More information about the Python-ideas mailing list