On Fri, Sep 23, 2016 at 5:54 AM Chris Angelico <rosuav@gmail.com> wrote:
On Fri, Sep 23, 2016 at 12:35 PM, Steven D'Aprano <steve@pearwood.info> wrote:
> The straight-forward and simple way of writing a recursive spam()
> function surprises beginners, but they might go years or their entire
> career without running into a situation where they are caught by
> surprise. After all, it is rare for productuon code to rename functions,
> and rarer still to do it to recursive functions:
>
>     func = spam
>     spam = something_else()
>     func()  # why does the recursion not work???
>
> In production code, that sort of thing almost never happens.

There's actually one very common technique involving rebinding functions.

@count_calls
def mergesort(lst):
    mid = len(lst) // 2
    if not mid: return lst
    return merge(mergesort(lst[..mid]), mergesort(lst[mid..]))

*Obviously* this is recursive. But if you used some magic that said
"call the function that's currently being called", you'd be bypassing
the count_calls decoration (which would presumably work by creating a
wrapper function). Yes, it may defeat some potential optimizations (eg
tail recursion optimization), but it enables all this flexibility.

So we _need_ to have this kind of rebind available, and not just for experts.


I think you are mixing levels of abstraction because you know how this is implemented. The user only sees "A function named mergesort decorated by count_calls". She does not see "A function named mergesort passed to a higher order function named count_calls whose result is bound into the variable mergesort". Even if the latter is exactly what happens, declaratively the former is more accurate by intention.
 
Ideally, the calls to mergesort will rebind to this _decorated_ function. not to the mutable global variable. Again, the argument that it will be very hard to implement it in a different way, or that is will break things, is a very strong argument, and I am not confronting it.

> In the meantime, I'll usually just write my recursive functions the
> old-fashioned normal way.

As will I. Of course, people are welcome to work differently, just as
long as I never have to write tests for their code, or refactor
anything into a decorator, or anything like that. I want the
POWAH!!!!! :)

As will I, simply because the old-fashioned way is more readable. And I will sadly accept the fact that I can't be 100% sure what's function is called at runtime. But _some_ people (medium-level, Steven, whose main language is probably not Python) will not even know this is the case.

Tests are important and could have reworked into the system (through inspect, or by using a special import which allow monkey patching). I can't see why the ability to test must remain in production.

Elazar