Expand the discussion of nested suite variants
This commit is contained in:
parent
1d9af129be
commit
66b59db2c8
167
pep-0403.txt
167
pep-0403.txt
|
@ -216,27 +216,6 @@ reference to the associated function or class definition without creating an
|
||||||
actual name binding in the current scope.
|
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
|
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 one potentially tricky part is changing the meaning of the references to
|
||||||
the statement local function or namespace while within the scope of the
|
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
|
``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
|
More Examples
|
||||||
|
@ -383,9 +364,32 @@ rather enlightening process of investigation.
|
||||||
Rejected Concepts
|
Rejected Concepts
|
||||||
=================
|
=================
|
||||||
|
|
||||||
A previous incarnation of this PEP (see [1]) proposed a much uglier syntax
|
To avoid retreading previously covered ground, some rejected alternatives
|
||||||
that (quite rightly) was not well received. The current proposal is
|
are documented in this section.
|
||||||
significantly easier both to read and write.
|
|
||||||
|
|
||||||
|
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
|
A more recent variant always used ``...`` for forward references, along
|
||||||
with genuinely anonymous function and class definitions. However, this
|
with genuinely anonymous function and class definitions. However, this
|
||||||
|
@ -403,11 +407,114 @@ cases::
|
||||||
a = calculate_a()
|
a = calculate_a()
|
||||||
b = calculate_b()
|
b = calculate_b()
|
||||||
|
|
||||||
Another past alternative omitted the ``@`` prefix. However, without that
|
|
||||||
prefix, the bare ``in`` keyword didn't associate the clause strongly
|
Using a nested suite
|
||||||
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.
|
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
|
References
|
||||||
==========
|
==========
|
||||||
|
|
Loading…
Reference in New Issue