diff --git a/pep-0393.txt b/pep-0393.txt index 35099d9b2..fd877ff6c 100644 --- a/pep-0393.txt +++ b/pep-0393.txt @@ -94,7 +94,7 @@ creation time are called "compact" unicode objects; character data immediately follow the base structure. If the maximum character is less than 128, they use the PyASCIIObject structure, and the UTF-8 data, the UTF-8 length and the wstr length are the same as the length -and the ASCII data. For non-ASCII strings, the PyCompactObject +of the ASCII data. For non-ASCII strings, the PyCompactObject structure is used. Resizing compact objects is not supported. Objects for which the maximum character is not given at creation time diff --git a/pep-0403.txt b/pep-0403.txt new file mode 100644 index 000000000..483a2d3fe --- /dev/null +++ b/pep-0403.txt @@ -0,0 +1,304 @@ +PEP: 403 +Title: Statement local classes and functions +Version: $Revision$ +Last-Modified: $Date$ +Author: Nick Coghlan +Status: Deferred +Type: Standards Track +Content-Type: text/x-rst +Created: 2011-10-13 +Python-Version: 3.x +Post-History: 2011-10-13 +Resolution: TBD + + +Abstract +======== + +This PEP proposes the addition of ':' as a new class and function prefix +syntax (analogous to decorators) that permits a statement local function or +class definition to be appended to any Python statement that currently does +not have an associated suite. + +In addition, the new syntax would allow the '@' symbol to be used to refer +to the statement local function or class without needing to repeat the name. + +When the ':' prefix syntax is used, the associated statement would be executed +*instead of* the normal local name binding currently implicit in function +and class definitions. + +This PEP is based heavily on many of the ideas in PEP 3150 (Statement Local +Namespaces) so some elements of the rationale will be familiar to readers of +that PEP. That PEP has now been withdrawn in favour of this one. + + +PEP Deferral +============ + +Like PEP 3150, this PEP currently exists in a deferred state. Unlike PEP 3150, +this isn't because I suspect it might be a terrible idea or see nasty problems +lurking in the implementation (aside from one potential parsing issue). + +Instead, it's because I think fleshing out the concept, exploring syntax +variants, creating a reference implementation and generally championing +the idea is going to require more time than I can give it in the 3.3 time +frame. + +So, it's deferred. If anyone wants to step forward to drive the PEP for 3.3, +let me know and I can add you as co-author and move it to Draft status. + + +Basic Examples +============== + +Before diving into the long history of this problem and the detailed +rationale for this specific proposed solution, here are a few simple +examples of the kind of code it is designed to simplify. + +As a trivial example, weakref callbacks could be defined as follows:: + + :x = weakref.ref(obj, @) + def report_destruction(obj): + print("{} is being destroyed".format(obj)) + +This contrasts with the current repetitive "out of order" syntax for this +operation:: + + def report_destruction(obj): + print("{} is being destroyed".format(obj)) + + x = weakref.ref(obj, report_destruction) + +That structure is OK when you're using the callable multiple times, but +it's irritating to be forced into it for one-off operations. + +Similarly, singleton classes could now be defined as:: + + :instance = @() + class OnlyOneInstance: + pass + +Rather than:: + + class OnlyOneInstance: + pass + + instance = OnlyOneInstance() + +And the infamous accumulator example could become:: + + def counter(): + x = 0 + :return @ + def increment(): + nonlocal x + x += 1 + return x + +Proposal +======== + +This PEP proposes the addition of an optional block prefix clause to the +syntax for function and class definitions. + +This block prefix would be introduced by a leading ``:`` and would be +allowed to contain any simple statement (including those that don't +make any sense in that context - while such code would be legal, +there wouldn't be any point in writing it). + +The decorator symbol ``@`` would be repurposed inside the block prefix +to refer to the function or class being defined. + +When a block prefix is provided, it *replaces* the standard local +name binding otherwise implicit in a class or function definition. + + +Background +========== + +The question of "multi-line lambdas" has been a vexing one for many +Python users for a very long time, and it took an exploration of Ruby's +block functionality for me to finally understand why this bugs people +so much: Python's demand that the function be named and introduced +before the operation that needs it breaks the developer's flow of thought. +They get to a point where they go "I need a one-shot operation that does +", and instead of being able to just *say* that, they instead have to back +up, name a function to do , then call that function from the operation +they actually wanted to do in the first place. Lambda expressions can help +sometimes, but they're no substitute for being able to use a full suite. + +Ruby's block syntax also heavily inspired the style of the solution in this +PEP, by making it clear that even when limited to *one* anonymous function per +statement, anonymous functions could still be incredibly useful. Consider how +many constructs Python has where one expression is responsible for the bulk of +the heavy lifting: + + * comprehensions, generator expressions, map(), filter() + * key arguments to sorted(), min(), max() + * partial function application + * provision of callbacks (e.g. for weak references) + * array broadcast operations in NumPy + +However, adopting Ruby's block syntax directly won't work for Python, since +the effectiveness of Ruby's blocks relies heavily on various conventions in +the way functions are *defined* (specifically, Ruby's ``yield`` syntax to +call blocks directly and the ``&arg`` mechanism to accept a block as a +functions final argument. + +Since Python has relied on named functions for so long, the signatures of +APIs that accept callbacks are far more diverse, thus requiring a solution +that allows anonymous functions to be slotted in at the appropriate location. + + +Relation to PEP 3150 +==================== + +PEP 3150 (Statement Local Namespaces) described its primary motivation +as being to elevate ordinary assignment statements to be on par with ``class`` +and ``def`` statements where the name of the item to be defined is presented +to the reader in advance of the details of how the value of that item is +calculated. This PEP achieves the same goal in a different way, by allowing +the simple name binding of a standard function definition to be replaced +with something else (like assigning the result of the function to a value). + +This PEP also achieves most of the other effects described in PEP 3150 +without introducing a new brainbending kind of scope. All of the complex +scoping rules in PEP 3150 are replaced in this PEP with the simple ``@`` +reference to the statement local function or class definition. + + +Symbol Choice +============== + +The ':' symbol was chosen due to its existing presence in Python and its +association with 'functions in expressions' via ``lambda`` expressions. The +past Simple Implicit Lambda proposal (PEP ???) was also a factor. + +The proposal definitely requires *some* kind of prefix to avoid parsing +ambiguity and backwards compatibility problems and ':' at least has the +virtue of brevity. There's no obious alternative symbol that offers a +clear improvement. + +Introducing a new keyword is another possibility, but I haven't come up +with one that really has anything to offer over the leading colon. + + +Syntax Change +============= + +Current:: + + atom: ('(' [yield_expr|testlist_comp] ')' | + '[' [testlist_comp] ']' | + '{' [dictorsetmaker] '}' | + NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False') + +Changed:: + + atom: ('(' [yield_expr|testlist_comp] ')' | + '[' [testlist_comp] ']' | + '{' [dictorsetmaker] '}' | + NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False' | '@') + +New:: + + blockprefix: ':' simple_stmt + block: blockprefix (decorated | classdef | funcdef) + +The above is the general idea, but I suspect that change to the 'atom' +definition would cause an ambiguity problem in the parser when it comes to +detecting decorator lines. So the actual implementation would be more complex +than that. + +Grammar: http://hg.python.org/cpython/file/default/Grammar/Grammar + + +Possible Implementation Strategy +================================ + +This proposal has one titanic advantage over PEP 3150: implementation +should be relatively straightforward. + +Both the class and function definition statements emit code to perform +the local name binding for their defined name. Implementing this PEP +should just require intercepting that code generation and replacing +it with the code in the block prefix. + +The one potentially tricky part is working out how to allow the dual +use of '@' without rewriting half the grammar definition. + +More Examples +============= + +Calculating attributes without polluting the local namespace (from os.py):: + + # Current Python (manual namespace cleanup) + def _createenviron(): + ... # 27 line function + + environ = _createenviron() + del _createenviron + + # Becomes: + :environ = @() + def _createenviron(): + ... # 27 line function + +Loop early binding:: + + # Current Python (default argument hack) + funcs = [(lambda x, i=i: x + i) for i in range(10)] + + # Becomes: + :funcs = [@(i) for i in range(10)] + def make_incrementor(i): + return lambda x: x + i + + # Or even: + :funcs = [@(i) for i in range(10)] + def make_incrementor(i): + :return @ + def incrementor(x): + return x + i + + +Reference Implementation +======================== + +None as yet. + + +TO DO +===== + +Sort out links and references to everything :) + + +Acknowledgements +================ + +Huge thanks to Gary Bernhardt for being blunt in pointing out that I had no +idea what I was talking about in criticising Ruby's blocks, kicking off a +rather enlightening process of investigation. + + +References +========== + +TBD + + +Copyright +========= + +This document has been placed in the public domain. + + +.. + Local Variables: + mode: indented-text + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + coding: utf-8 + End: diff --git a/pep-3150.txt b/pep-3150.txt index 4493f9f1b..911707647 100644 --- a/pep-3150.txt +++ b/pep-3150.txt @@ -3,7 +3,7 @@ Title: Statement local namespaces (aka "given" clause) Version: $Revision$ Last-Modified: $Date$ Author: Nick Coghlan -Status: Deferred +Status: Withdrawn Type: Standards Track Content-Type: text/x-rst Created: 2010-07-09 diff --git a/pep-3151.txt b/pep-3151.txt index 32e58dc7e..a78f773d6 100644 --- a/pep-3151.txt +++ b/pep-3151.txt @@ -193,10 +193,14 @@ Then we can define *useful compatibility* as follows: * useful compatibility doesn't make exception catching any narrower, but it can be broader for *careless* exception-catching code. Given the following kind of snippet, all exceptions caught before this PEP will also be - caught after this PEP, but the reverse may be false:: + caught after this PEP, but the reverse may be false (because the coalescing + of ``OSError``, ``IOError`` and others means the ``except`` clause throws + a slightly broader net):: try: + ... os.remove(filename) + ... except OSError: pass