PEP 622: Added rejected ideas section comparing with traditional OOP (#1504)

Co-authored-by: Brandt Bucher <brandtbucher@gmail.com>
This commit is contained in:
Talin 2020-07-07 19:35:05 -07:00 committed by GitHub
parent 26ac4b3d3e
commit 382f0dfc7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 60 additions and 11 deletions

View File

@ -202,7 +202,7 @@ separates alternative patterns (not unlike regular expressions or EBNF
grammars), and ``_`` is a wildcard.
In some occasions, extraction of information is not as relevant as
identifying structure. Take the following example from the
identifying structure. Take the following example from the
`Python standard library
<https://github.com/python/cpython/blob/c4cacc8/Lib/lib2to3/fixer_util.py#L158>`_::
@ -297,11 +297,11 @@ functionality that is useful now but conservative. More patterns can be added
later as this feature gets more widespread use. See the `rejected ideas`_
and `deferred ideas`_ sections for more details.
The patterns listed here are described in more detail below, but summarized
The patterns listed here are described in more detail below, but summarized
together in this section for simplicity:
- A **literal pattern** is useful to filter constant values in a structure.
It looks like a Python literal (including some values like ``True``,
It looks like a Python literal (including some values like ``True``,
``False`` and ``None``). It only matches objects equal to the literal, and
never binds.
- A **capture pattern** looks like ``x`` and is equivalent to an identical
@ -309,12 +309,12 @@ together in this section for simplicity:
with the given name.
- The **wildcard pattern** is a single underscore: ``_``. It always matches,
but does not capture any variable (which prevents interference with other
uses for ``_`` and allows for some optimizations).
uses for ``_`` and allows for some optimizations).
- A **constant value pattern** works like the literal but for certain named
constants. Note that it must be a qualified (dotted) name, given the possible
ambiguity with a capture pattern. It looks like ``Color.RED`` and
only matches values equal to the corresponding value. It never binds.
- A **sequence pattern** looks like ``[a, *rest, b]`` and is similar to
- A **sequence pattern** looks like ``[a, *rest, b]`` and is similar to
a list unpacking. An important difference is that the elements nested
within it can be any kind of patterns, not just names or sequences.
It matches only sequences of appropriate length, as long as all the sub-patterns
@ -526,7 +526,7 @@ the ``""`` case clause was taken::
... # but works fine if greeting was not empty
While matching against each case clause, a name may be bound at most
once, having two capture patterns with coinciding names is an error::
once, having two capture patterns with coinciding names is an error::
match data:
case [x, x]: # Error!
@ -537,7 +537,7 @@ Also, ``[x, y] | Point(x, y)`` is a legal pattern because the two
alternatives are never matched at the same time.
The single underscore (``_``) is not considered a ``NAME`` and treated specially
as a `wildcard pattern`_.
as a `wildcard pattern`_.
Reminder: ``None``, ``False`` and ``True`` are keywords denoting
literals, not names.
@ -603,8 +603,8 @@ Sequence Patterns
Simplified syntax::
sequence_pattern:
| '[' [values_pattern] ']'
| '(' [value_pattern ',' [values pattern]] ')'
| '[' [values_pattern] ']'
| '(' [value_pattern ',' [values pattern]] ')'
values_pattern: ','.value_pattern+ ','?
value_pattern: '*' capture_pattern | pattern
@ -1315,6 +1315,55 @@ above illustrate this comparison well enough. For more real code examples
and their translations see Ref. [7]_.
Don't do this, use existing method dispatching mechanisms
---------------------------------------------------------
We recognize that some of the use cases for the ``match`` statement overlap
with what can be done with traditional object-oriented programming (OOP) design
techniques using class inheritance. The ability to choose alternate
behaviors based on testing the runtime type of a match subject might
even seem heretical to strict OOP purists.
However, Python has always been a language that embraces a variety of
programming styles and paradigms. Classic Python design idioms such as
"duck"-typing go beyond the traditional OOP model.
We believe that there are important use cases where the use of ``match`` results
in a cleaner and more maintainable architecture. These use cases tend to
be characterized by a number of features:
* Algorithms which cut across traditional lines of data encapsulation. If an
algorithm is processing heterogenous elements of different types (such as
evaluating or transforming an abstract syntax tree, or doing algebraic
manipulation of mathematical symbols), forcing the user to implement
the algorithm as individual methods on each element type results in
logic that is smeared across the entire codebase instead of being neatly
localized in once place.
* Program architectures where the set of possible data types is relatively
stable, but there is an ever-expanding set of operations to be performed
on those data types. Doing this in a strict OOP fashion requires constantly
adding new methods to both the base class and subclasses to support the new
methods, "polluting" the base class with lots of very specialized method
definitions, and causing widespread disruption and churn in the code. By
contrast, in a ``match``-based dispatch, adding a new behavior merely
involves writing a new ``match`` statement.
* OOP also does not handle dispatching based on the *shape* of an object, such
as the length of a tuple, or the presence of an attribute -- instead any such
dispatching decision must be encoded into the object's type. Shape-based
dispatching is particularly interesting when it comes to handling "duck"-typed
objects.
Where OOP is clearly superior is in the opposite case: where the set of possible
operations is relatively stable and well-defined, but there is an ever-growing
set of data types to operate on. A classic example of this is UI widget toolkits,
where there is a fixed set of interaction types (repaint, mouse click, keypress,
and so on), but the set of widget types is constantly expanding as developers
invent new and creative user interaction styles. Adding a new kind of widget
is a simple matter of writing a new subclass, whereas with a match-based approach
you end up having to add a new case clause to many widespread match statements.
We therefore don't recommend using ``match`` in such a situation.
Allow more flexible assignment targets instead
----------------------------------------------
@ -1898,7 +1947,7 @@ Other pattern-based constructions
---------------------------------
Many other languages supporting pattern-matching use it as a basis for multiple
language constructs, including a matching operator, a generalized form
language constructs, including a matching operator, a generalized form
of assignment, a filter for loops, a method for synchronizing communication,
or specialized if statements. Some of these were mentioned in the discussion
of the first draft. Another question asked was why this particular form (joining
@ -2093,7 +2142,7 @@ Version History
- Drop ``ImpossibleMatchError`` exception
- Drop leading dot for loads (moved to `deferred ideas`_)
- Reworked the initial sections (everything before `syntax`_)
- Added an overview of all the types of patterns before the
- Added an overview of all the types of patterns before the
detailed description
- Added simplified syntax next to the description of each pattern
- Separate description of the wildcard from capture patterns