Thanks for taking the time to work on this, Mark. Overall, I'm skeptical of this proposal. It seems like it takes a lot of "simple" things and makes them quite complex, and takes many "static" things and makes them quite dynamic. I feel that it also misrepresents certain aspects of PEP 634.
Here's a more detailed commentary: > Pattern matching will be more usable for complex classes, by allowing classes > more control over which patterns they match. I fear this is at the expense of most simple classes, which currently "just work" with PEP 634. I'm not convinced that making it easier for very complicated classes (like those in `sympy`) to participate at the expense of everyday classes is a win. For comparison, here is what (as I understand it) each PEP requires for a class to be used in a pattern such as `C(x, y, z)`: ```py class C: """A PEP 634 matchable class.""" __match_args__ = ... class C: """A PEP 653 matchable class.""" __match_kind__ = MATCH_CLASS __attributes__ = ... def __deconstruct__(self): ... ``` For a pattern with no positional subpatterns, such as like `C()` or `C(x=x, y=y, z=z)`: ```py class C: """A PEP 634 matchable class (all classes work).""" class C: """A PEP 653 matchable class.""" __match_kind__ = MATCH_CLASS __attributes__ = ... def __deconstruct__(self): ... ``` It also appears that we lose a lot of expressive "idioms" by requiring `__attributes__` to be complete. For example, the following attribute extractions work with PEP 634, but not PEP 653. ```py match subject: case object(a=_): # Match any object with an "a" attribute. ... case object(x=x, y=y, z=z): # Match any object with "x", "y", and "z" attributes, extracting them. ... ``` This also means that matching classes like `types.SimpleNamespace` are much less powerful under PEP 653, since the class must know which attributes are "allowed" to be looked up. Further, this whole specification shifts most of the matching logic from the class in the pattern to the subject itself: ```py match ChildA(), ChildB(): case Parent(a, b), Parent(x, y): ... ``` The above pattern could have completely different rules for extracting `a` and `b` vs `x` and `y` if either child overrides `__deconstruct__`. I think that is a mistake. > PEP 634 also privileges some builtin classes with a special form of matching, > the "self" match. For example the pattern `list(x)` matches a list and > assigns the list to `x`. By allowing classes to choose which kinds of pattern > they match, other classes can use this form as well. This is already fairly trivial to implement: ```py class C: __match_args__ = ("_self",) _self = property(lambda s: s) ``` You can even avoid adding a `_self` attribute if you do some magic with descriptors... ;). We could consider provide a decorator for this somewhere in the stdlib if there's demonstrated need (but I'm not sure there will be). > All classes should ensure that the the value of `__match_kind__` follows the > specification. Therefore, implementations can assume, without checking, that > all the following are false: > `(__match_kind__ & (MATCH_SEQUENCE | MATCH_MAPPING)) == (MATCH_SEQUENCE | > MATCH_MAPPING)` > `(__match_kind__ & (MATCH_SELF | MATCH_CLASS)) == (MATCH_SELF | MATCH_CLASS)` > `(__match_kind__ & (MATCH_SELF | MATCH_DEFAULT)) == (MATCH_SELF | > MATCH_DEFAULT)` > `(__match_kind__ & (MATCH_DEFAULT | MATCH_CLASS)) == (MATCH_DEFAULT | > MATCH_CLASS)` Oof. I was scratching my head for way too long at this before I noticed the previous sentence said "false". Maybe reword this section to indicate the conditions that hold *true*? :) > Security Implications > Preventing the possible registering or unregistering of classes as sequences > or a mappings, that PEP 634 allows, should improve security. However, the > advantage is slight and is not a motivation for this PEP. I would say the advantage is nonexistent. How is this any different than tweaking the flags in a class's `__match_kind__`? > Efficient Implementation / Implementation I won't get too deep into this section, but on the surface most flavors of implementation/optimization present here are also possible with PEP 634. I understand that you feel there are benefits to having "rules" for what optimizations are legal, but we don't need to completely change the mechanics of the match statement in order to do so. > Because the object model is a core part of Python, implementations already > handle special attribute lookup efficiently. Looking up a special attribute > is much faster than performing a subclass test on an abstract base class. But calling a `__deconstruct__` method whenever positional arguments are present will slow down normal class matches, right? I see it as mostly a wash. > The changes to the semantics can be summarized as: > - Selecting the kind of pattern uses `cls.__match_kind__` instead of > `issubclass(cls, collections.abc.Mapping)` and `issubclass(cls, > collections.abc.Sequence)` and allows classes control over which kinds of > pattern they match. > - Class matching is via the `__attributes__` attribute and `__deconstruct__` > method, rather than the `__match_args__` method, and allows classes more > control over how they are deconstructed. In general, it seems to me that this proposal prioritizes: - complicated library code - ease of implementation in the interpreter over: - simple client code - general usability I can't say I support that, generally. When working on PEP 634, we tossed lot of more complicated, powerful protocols (like `__match__` and several others before it... don't mention the phrase "match proxy" around any of the authors!) in favor of simplicity. This sort of feels like a rehash of that. Brandt _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/ZXKAKV62YPKP5JYOV7EFY62MGCAW2BZ3/ Code of Conduct: http://python.org/psf/codeofconduct/