|
My problem is that given the following examples, only 3 can be eliminated even though there's no visible marker: return f(a,b,c)
return f(a,b,c) or False
try: return f(a,b,c)
finally: os.remove(tempfilename)
return [north,south,west,east][dir]()
return a[0]
In the presence of try (also with, and perhaps a few others I can't think of right now) even the examples you gave cannot be TCEd.Personally, I consider broken a language in which enclosing a construct with "try: .... finally: pass" to switch between constant memory and endless memory requirements. That is already true for scheme in a way. Guido's refusal to break python in this sense is wholly supported by me, even if that's not his reason to refuse. And "return from" suggested by another poster is the perfect pythonic syntax for such a thing. And I definitely thing it is a property of the specific call site, NOT the entire function, whether it should be checked for tailness. Decorators are the wrong solution here. |
Previously, you argued that Python semantics makes it very clear that all costs are visible. This seems to be against any rewrite rule, as the visible cost ("or False", "+ 0", "finally: pass") suddenly disappear. You are using your own rebuttal as an argument to make tailcalls look harder to analyse! They are not.
> And I definitely thing it is a property of the specific call site, NOT the entire function, whether it should be checked for tailness. Decorators are the wrong solution here.
As a caller of a function, the property you care about is "does it consumes a fixed amount of stack?". As the writer of this function, that's the thing that matters in the end: Sure, that means checking each tail calls, but you really want the global guarantee that you didn't forgot one. The local explicit annotation becomes redundant.