Improve tail call optimization documentation
See original GitHub issueCoconut: Version 1.3.1 [Dead Parrot] running on Python 3.6.3
Consider factorial written in a tail recursive form
def factorial(n, acc=1):
if n == 1:
return acc
else:
return factorial(n-1, acc*n)
When --no-tco
flag is disabled this compiles to:
@_coconut_tco
def factorial(n, acc=1):
def _coconut_mock_func(n, acc=1): return n, acc
while True:
if n == 1:
return acc
else:
if factorial is _coconut_recursive_func_0:
n, acc = _coconut_mock_func(n - 1, acc * n)
continue
else:
return _coconut_tail_call(factorial, n - 1, acc * n)
return None
_coconut_recursive_func_0 = factorial
When the --no-tco
flag is enabled this compiles to:
def factorial(n, acc=1):
def _coconut_mock_func(n, acc=1): return n, acc
while True:
if n == 1:
return acc
else:
if factorial is _coconut_recursive_func_0:
n, acc = _coconut_mock_func(n - 1, acc * n)
continue
else:
return factorial(n - 1, acc * n)
return None
_coconut_recursive_func_0 = factorial
Aren’t both the versions tail call optimized? The only difference is that the first version is optimized for all tails calls including mutual recursion whereas the second case only handles tail recursion elimination. Is that correct? I just felt that the documentation for this was not very clear since tail recursion elimination happens even with --no-tco
.
My original question is: Is there a reason the following does not undergo tail recursion elimination on compilation?
def rec_dict(init_dict, n):
if n == 1:
return init_dict()
else:
return rec_dict(-> defaultdict(init_dict), n - 1)
where init_dict = () -> defaultdict(set)
Issue Analytics
- State:
- Created 6 years ago
- Comments:9 (4 by maintainers)
Top Results From Across the Web
27. Tail call optimization
ECMAScript 6 offers tail call optimization, where you can make some function calls without growing the call stack. This chapter explains how that...
Read more >Demystifying Tail Call Optimization
Tail call optimization reduces the space complexity of recursion from O(n) to O(1) . Our function would require constant memory for execution.
Read more >Managed Resources · Enso Developer Documentation
Tail call optimization is a powerful technique for optimizing functional programs. It allows transforming recursive functions of certain shapes into loops, ...
Read more >Chapter 8 - Tail Call Optimization
This chapter explores a technique called tail call optimization, which is a feature provided by a compiler or interpreter to avoid stack overflows....
Read more >Tail Call Elimination
Tail call elimination reduces the space complexity of recursion from O(N) to O(1). As function call is eliminated, no new stack frames are ......
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
@evhub when using the kernel
@dileep-kishore The
--no-tco
flag disables tail call optimization, but not tail recursion elimination, as you’ve noticed. The reason for this is that Coconut’s tail recursion elimination is (usually) faster than doing nothing, while Coconut’s tail call optimization is (usually) slower, thus--no-tco
keeps the one that leads to faster code and removes the one that leads to slower code. Mutual recursion falls into the tail call optimization not the tail recursion elimination category, thus it doesn’t get optimized if--no-tco
is passed. I should definitely make the docs more clear on this.As for your specific example, the fact that Coconut doesn’t do tail recursion elimination there is because of the lambda. Unfortunately, the way Python handles lambdas is that they store a pointer to (not a copy of!) the namespace enclosing them. Thus, if Coconut tries to re-use the namespace that produced that lambda to do tail recursion elimination, the lambda gets retroactively changed! For example, take a look at this:
This is really unfortunate for doing functional programming in Python, and I haven’t yet thought of a good way to fix it other than just disabling tail recursion elimination if Coconut sees a lambda. Currently, this is undocumented behavior, so at the very least I will definitely add something on this to the docs.