Expand the discussion of nested suite variants

This commit is contained in:
Nick Coghlan 2012-09-04 21:33:39 +10:00
parent 1d9af129be
commit 66b59db2c8
1 changed files with 137 additions and 30 deletions

View File

@ -216,27 +216,6 @@ reference to the associated function or class definition without creating an
actual name binding in the current scope.
Why not a nested suite?
-----------------------
The problem with using a full nested suite is best described by reviewing
PEP 3150. It's ridiculously hard to implement properly, and creates way
too many situations where there are two ways to do it (almost any construct
that can be expressed with ordinary imperative code could instead be
expressed using a given statement).
By contrast, the decorator inspired syntax explicitly limits the new
feature to cases where it should actually improve readability, rather than
harming it. As in the case of the original introduction of decorators, the
idea of this new syntax is that if it *can* be used (i.e. the local name
binding of the function is completely unnecessary) then it *should* be used.
While a non-decorator based alternative could be considered (e.g. a nested
"suite" that actually allowed only a single class or function definition),
it seems excessive to introduce a completely new concept when it is
possible to use a variant of the existing decorator syntax instead.
Keyword Choice
--------------
@ -300,7 +279,9 @@ operation normally implied by a function or class definition.
The one potentially tricky part is changing the meaning of the references to
the statement local function or namespace while within the scope of the
``in`` statement, but that shouldn't be too hard to address by maintaining
some additional state within the compiler.
some additional state within the compiler (it's much easier to handle this
for a single name than it is for an unknown number of names in a full
nested suite).
More Examples
@ -383,9 +364,32 @@ rather enlightening process of investigation.
Rejected Concepts
=================
A previous incarnation of this PEP (see [1]) proposed a much uglier syntax
that (quite rightly) was not well received. The current proposal is
significantly easier both to read and write.
To avoid retreading previously covered ground, some rejected alternatives
are documented in this section.
Omitting the decorator prefix character
---------------------------------------
Earlier versions of this proposal omitted the ``@`` prefix. However, without
that prefix, the bare ``in`` keyword didn't associate the clause strongly
enough with the subsequent function or class definition. Reusing the
decorator prefix and explicitly characterising the new construct as a kind
of decorator clause is intended to help users link the two concepts and
see them as two variants of the same idea.
Anonymous Forward References
----------------------------
A previous incarnation of this PEP (see [1]) proposed a syntax where the
new clause was introduced with ``:`` and the forward reference was written
using ``@``. Feedback on this variant was almost universally
negative, as it was considered both ugly and excessively magical::
:x = weakref.ref(target, @)
def report_destruction(obj):
print("{} is being destroyed".format(obj))
A more recent variant always used ``...`` for forward references, along
with genuinely anonymous function and class definitions. However, this
@ -403,11 +407,114 @@ cases::
a = calculate_a()
b = calculate_b()
Another past alternative omitted the ``@`` prefix. However, without that
prefix, the bare ``in`` keyword didn't associate the clause strongly
enough with the subsequent function or class definition. Reusing the
decorator prefix and explicitly characterising the new construct as a kind
of decorator clause should address that problem.
Using a nested suite
--------------------
The problems with using a full nested suite are best described in
PEP 3150. It's comparatively difficult to implement properly, the scoping
semantics are hard to explain and it creates quite a few situations where
there are two ways to do it without clear guidelines for choosing between
them (as almost any construct that can be expressed with ordinary imperative
code could instead be expressed using a given statement). While the PEP did
propose some new PEP 8 guidelines to help address that last problem, the
difficulties in implementation and explanation are not so easily dealt with.
By contrast, the decorator inspired syntax in this PEP explicitly limits the
new feature to cases where it should actually improve readability, rather
than harming it. As in the case of the original introduction of decorators,
the idea of this new syntax is that if it *can* be used (i.e. the local name
binding of the function is completely unnecessary) then it *should* be used.
Another possible variant of this idea is to keep the decorator based
*semantics* of this PEP, while adopting the prettier syntax from PEP 3150::
x = weakref.ref(target, report_destruction) given:
def report_destruction(obj):
print("{} is being destroyed".format(obj))
There are a couple of problems with this approach. The main issue is that
this syntax variant uses something that looks like a suite, but really isn't
one. A secondary concern is that it's not clear how the compiler will know
which name(s) in the leading expression are forward references (although
that could potentially be addressed through a suitable definition of the
suite-that-is-not-a-suite in the languge grammar).
There's yet another possibility which would require names to be explicitly
lifted out of the private namespace, eliminating much of the implementation
complexity discussed in PEP 3150 (as only identifiers would be exported,
rather than arbitrary subexpressions)::
x = weakref.ref(target, report_destruction) given report_destruction from:
def report_destruction(obj):
print("{} is being destroyed".format(obj))
or even::
x = weakref.ref(target, f) given report_destruction as f from:
def report_destruction(obj):
print("{} is being destroyed".format(obj))
This approach actually has much to recommend it. It's as powerful as
PEP 3150 while being substantially simpler both to implement and to
explain. The main downside is that it requires even *more* repetition
of names than is needed with the proposed ``@in`` decorator based syntax
or with the status quo where the function is defined and bound to a local
name prior to its sole use.
Some more examples using this last variant::
sorted_list = sorted(original, key=f) given f from:
def f(item):
try:
return item.calc_sort_order()
except NotSortableError:
return float('inf')
funcs = [adder(i) for i in range(10)] given adder from:
def adder(i):
return lambda x: x + i
environ = _createenviron() given _createenviron from:
def _createenviron():
... # 27 line function
c = math.sqrt(a*a + b*b) given a, b from:
a = calculate_a()
b = calculate_b()
dispatch[MyClass] = f given f from:
def f():
...
f() given f from:
def f():
...
One interesting outcome of such an approach is that it allows the semantics
of function decoration to be represented almost exactly as a specific
case of the new syntax::
# @deco2
# @deco1
# def name():
# ...
name = f given f from:
# Decorator expressions are evaluated first, in code order
_d2 = deco2
_d1 = deco1
# The base function object is defined
def name():
...
# Decorators are applied innermost first
f = _d2(_d1(name))
Hmm, perhaps PEP 3150 isn't *quite* as dead as I thought, although the
triple repetition of names is still a rather large downside.
References
==========