[Python-ideas] ScopeGuardStatement/Defer Proposal

Nathan Rice nathan.alexander.rice at gmail.com
Sun Feb 19 05:31:31 CET 2012


> The core problem comes down to the differences between Guido's
> original PEP 340 idea (which was much closer in power to Ruby's
> blocks, since it was a new looping construct that allowed 0-or-more
> executions of the contained block) and the more constrained with
> statement that is defined in PEP 343 (which will either execute the
> body once or throw an exception, distinguishing it clearly from both
> the looping constructs and if statements).
>
> The principle Guido articulated when making that decision was:
> "different forms of flow control should look different at the point of
> invocation".
>
> So, where a language like Ruby just defines one protocol (callbacks,
> supplemented by anonymous blocks that run directly in the namespace of
> the containing function) and uses it for pretty much *all* flow
> control (including all their loop constructs), Python works the other
> way around, defining *different* protocols for different patterns of
> invocation.
>
> This provides a gain in readability on the Python side. When you see
> any of the following in Python:
>
>   @whatever()
>   def f():
>        pass
>
>    with whatever():
>        # Do something!
>
>    for x in whatever():
>        # Do something!
>
> It places a lot of constraints on the nature of the object returned by
> "whatever()" - even without knowing anything else about it, you know
> the first must return a decorator, the second a context manager, and
> the third an iterable. If that's all you need to know at this point in
> time, you don't need to worry about the details - the local syntax
> tells you the important things you need to know about the flow
> control.

I can appreciate the intention there.  That particular case isn't as
big a deal from my perspective, my non-local code pain points tend to
be centered around boneheaded uses of inheritance and dynamic
modification of classes..

> In Ruby, though, all of them (assuming it isn't actually important
> that the function name be bound locally) could be written like this:
>
>    whatever() do:
>        # Do something!
>    end
>
> Is it a context manager? An iterable? Some other kind of callback?
> There's nothing in the syntax to tell you that - you're relying on
> naming conventions to provide that information (like the ".foreach"
> convention for iteration methods). That approach can obviously work
> (otherwise Ruby wouldn't be as popular as it is), but it *does* make
> it harder to pick up a piece of code and understand the possible
> control flows without looking elsewhere.

More often than not, when I am reading other people's code, I am
debugging it (and thus have local/global context information) or just
interested in nailing down a poorly documented corner of an API.  I
think if I were regularly in the habit of working with lots of
undocumented code I would probably appreciate this more.

> However, this decision to be explicit about flow control for the
> benefit of the *reader* brings with it a high *cost* on the Python
> side for the code *author*: where Ruby works by defining a nice syntax
> and semantics for callback based programming and building other
> language constructs on top of that, Python *doesn't currently have* a
> particularly nice general purpose native syntax for callback based
> programming.
>
> Decorators do work in many cases (especially simple callback
> registration), but they sometimes feel wrong because they're mainly
> designed to modify how a function is defined, not implement key
> program flow control constructs. However, their flexibility shouldn't
> be underestimated, and the CallbackStack API is designed to help
> Python developers push decorators and context managers closer to those
> limits *without* needing new language constructs. By decoupling the
> callback stack from the code layout, it gives you full *programmatic*
> control of the kinds of things context managers can help with when you
> know in advance exactly what you want to do.
>
> *If* CallbackStack proves genuinely popular (and given the number of
> proposals I have seen along these lines, and the feedback I have
> received on ContextStack to date, I expect it will), and people start
> to develop interesting patterns for using it, *then* we can start
> looking at the possibility of dedicated syntax to streamline
> particular use cases (just as the with statement itself was designed
> to streamline various use cases of the more general try statement).

I wasn't suggesting syntax needs to change necessarily, all the pieces
are already there.  I see it more along the lines of function.Event,
class.Event, module.Event, context_manager.Event, etc.  It is a moot
point though.

Thank you again for taking the time to clarify the rational for me.
It wasn't intuitive to me because it does not really address issues I
have.



More information about the Python-ideas mailing list