Switch PEP 403 from : and @ symbols to postdef and def keywords

This commit is contained in:
Nick Coghlan 2011-10-13 21:30:11 +10:00
parent 3377fbf36a
commit 7b0a3e1a8b
1 changed files with 80 additions and 58 deletions

View File

@ -1,5 +1,5 @@
PEP: 403 PEP: 403
Title: Statement local classes and functions Title: Prefix syntax for post function definition operations
Version: $Revision$ Version: $Revision$
Last-Modified: $Date$ Last-Modified: $Date$
Author: Nick Coghlan <ncoghlan@gmail.com> Author: Nick Coghlan <ncoghlan@gmail.com>
@ -15,17 +15,17 @@ Resolution: TBD
Abstract Abstract
======== ========
This PEP proposes the addition of ':' as a new class and function prefix This PEP proposes the addition of ``postdef`` as a new function prefix
syntax (analogous to decorators) that permits a statement local function or syntax (analogous to decorators) that permits the execution of a single simple
class definition to be appended to any Python statement that currently does statement (potentially including substatements separated by semi-colons) after
not have an associated suite.
In addition, the new syntax would allow the '@' symbol to be used to refer In addition, the new syntax would allow the 'def' keyword to be used to refer
to the statement local function or class without needing to repeat the name. to the function being defined without needing to repeat the name.
When the ':' prefix syntax is used, the associated statement would be executed When the 'postdef' prefix syntax is used, the associated statement would be
*instead of* the normal local name binding currently implicit in function executed *in addition to* the normal local name binding implicit in function
and class definitions. definitions. Any name collision are expected to be minor, analagous to those
encountered with ``for`` loop iteration variables.
This PEP is based heavily on many of the ideas in PEP 3150 (Statement Local 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 Namespaces) so some elements of the rationale will be familiar to readers of
@ -57,7 +57,7 @@ examples of the kind of code it is designed to simplify.
As a trivial example, weakref callbacks could be defined as follows:: As a trivial example, weakref callbacks could be defined as follows::
:x = weakref.ref(obj, @) postdef x = weakref.ref(target, def)
def report_destruction(obj): def report_destruction(obj):
print("{} is being destroyed".format(obj)) print("{} is being destroyed".format(obj))
@ -67,33 +67,39 @@ operation::
def report_destruction(obj): def report_destruction(obj):
print("{} is being destroyed".format(obj)) print("{} is being destroyed".format(obj))
x = weakref.ref(obj, report_destruction) x = weakref.ref(target, report_destruction)
That structure is OK when you're using the callable multiple times, but 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. it's irritating to be forced into it for one-off operations.
Similarly, singleton classes could now be defined as:: Similarly, a sorted operation on a particularly poorly defined type could
now be defined as::
:instance = @() postdef sorted_list = sorted(original, key=def)
class OnlyOneInstance: def force_sort(item):
pass try:
return item.calc_sort_order()
except NotSortableError:
return float('inf')
Rather than:: Rather than::
class OnlyOneInstance: def force_sort(item):
pass try:
return item.calc_sort_order()
except NotSortableError:
return float('inf')
instance = OnlyOneInstance() sorted_list = sorted(original, key=force_sort)
And the infamous accumulator example could become:: And early binding semantics in a list comprehension could be attained via::
postdef funcs = [def(i) for i in range(10)]
def make_incrementor(i):
postdef return def
def incrementor(x):
return x + i
def counter():
x = 0
:return @
def increment():
nonlocal x
x += 1
return x
Proposal Proposal
======== ========
@ -101,16 +107,19 @@ Proposal
This PEP proposes the addition of an optional block prefix clause to the This PEP proposes the addition of an optional block prefix clause to the
syntax for function and class definitions. syntax for function and class definitions.
This block prefix would be introduced by a leading ``:`` and would be This block prefix would be introduced by a leading ``postdef`` and would be
allowed to contain any simple statement (including those that don't allowed to contain any simple statement (including those that don't
make any sense in that context - while such code would be legal, make any sense in that context - while such code would be legal,
there wouldn't be any point in writing it). there wouldn't be any point in writing it). This permissive structure is
easier to define and easier to explain, but a more restrictive approach that
only permits operations that "make sense" would also be possible (see PEP
3150 for a list of possible candidates)
The decorator symbol ``@`` would be repurposed inside the block prefix The function definition keyword ``def`` would be repurposed inside the block prefix
to refer to the function or class being defined. to refer to the function being defined.
When a block prefix is provided, it *replaces* the standard local When a block prefix is provided, the standard local name binding implicit
name binding otherwise implicit in a class or function definition. in the function definition still takes place.
Background Background
@ -143,7 +152,7 @@ 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 effectiveness of Ruby's blocks relies heavily on various conventions in
the way functions are *defined* (specifically, Ruby's ``yield`` syntax to the way functions are *defined* (specifically, Ruby's ``yield`` syntax to
call blocks directly and the ``&arg`` mechanism to accept a block as a call blocks directly and the ``&arg`` mechanism to accept a block as a
functions final argument. function's final argument).
Since Python has relied on named functions for so long, the signatures of 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 APIs that accept callbacks are far more diverse, thus requiring a solution
@ -163,24 +172,35 @@ 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 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 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 ``@`` scoping rules in PEP 3150 are replaced in this PEP with the simple ``def``
reference to the statement local function or class definition. reference to the associated function definition.
Symbol Choice Keyword 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 The proposal definitely requires *some* kind of prefix to avoid parsing
ambiguity and backwards compatibility problems and ':' at least has the ambiguity and backwards compatibility problems with existing constructs.
virtue of brevity. There's no obious alternative symbol that offers a It also needs to be clearly highlighted to readers, since it declares that
clear improvement. the following piece of code is going to be executed out of order.
Introducing a new keyword is another possibility, but I haven't come up The 'postdef' keyword was chosen as a literal explanation of exactly what
with one that really has anything to offer over the leading colon. the new clause does: execute the specified statement *after* the associated
function definition, even though it is physically written *before* the
definition in the source code.
Requirement to Name Functions
=============================
One of the objections to widespread use of lambda expressions is that they
have an atrocious effect on traceback intelligibility and other aspects of
introspection. Accordingly, this PEP requires that even throwaway functions
be given some kind of name.
To help encourage the use of meaningful names without users having to repeat
themselves, the PEP suggests the provision of the ``def`` shorthand reference
to the current function from the ``postdef`` clause.
Syntax Change Syntax Change
@ -198,17 +218,17 @@ Changed::
atom: ('(' [yield_expr|testlist_comp] ')' | atom: ('(' [yield_expr|testlist_comp] ')' |
'[' [testlist_comp] ']' | '[' [testlist_comp] ']' |
'{' [dictorsetmaker] '}' | '{' [dictorsetmaker] '}' |
NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False' | '@') NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False' | 'def')
New:: New::
blockprefix: ':' simple_stmt blockprefix: 'postdef' simple_stmt
block: blockprefix (decorated | classdef | funcdef) block: blockprefix funcdef
The above is the general idea, but I suspect that change to the 'atom' The above is the general idea, but I suspect that the change to the 'atom'
definition would cause an ambiguity problem in the parser when it comes to definition may cause an ambiguity problem in the parser when it comes to
detecting decorator lines. So the actual implementation would be more complex detecting function definitions. So the actual implementation may need to be
than that. more complex than that.
Grammar: http://hg.python.org/cpython/file/default/Grammar/Grammar Grammar: http://hg.python.org/cpython/file/default/Grammar/Grammar
@ -219,13 +239,12 @@ Possible Implementation Strategy
This proposal has one titanic advantage over PEP 3150: implementation This proposal has one titanic advantage over PEP 3150: implementation
should be relatively straightforward. should be relatively straightforward.
Both the class and function definition statements emit code to perform The post definition statement can be incorporated into the AST for the
the local name binding for their defined name. Implementing this PEP function node and simply visited out of sequence.
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 The one potentially tricky part is working out how to allow the dual
use of '@' without rewriting half the grammar definition. use of 'def' without rewriting half the grammar definition.
More Examples More Examples
============= =============
@ -273,6 +292,9 @@ TO DO
Sort out links and references to everything :) Sort out links and references to everything :)
Start of python-ideas thread:
http://mail.python.org/pipermail/python-ideas/2011-October/012276.html
Acknowledgements Acknowledgements
================ ================