On Wed, Mar 31, 2021 at 12:08 PM Mark Shannon <mark@hotpy.org> wrote:
Hi Guido,

On 31/03/2021 6:21 pm, Guido van Rossum wrote:
> On Wed, Mar 31, 2021 at 2:30 AM Mark Shannon <mark@hotpy.org
> <mailto:mark@hotpy.org>> wrote:
 
[Brandt Bucher, earlier]
>      > - Add new `__match_seq__` and `__match_map__` special attributes,
>     corresponding to new public `Py_TPFLAGS_MATCH_SEQ` and
>     `Py_TPFLAGS_MATCH_MAP` flags for use in `tp_flags`. When Python
>     classes are defined with one or both of these attributes set to a
>     boolean value, `type.__new__` will update the flags on the type to
>     reflect the change (using a similar mechanism as `__slots__`
>     definitions). They will be inherited otherwise. For convenience,
>     `collections.abc.Sequence` will define `__match_seq__ = True`, and
>     `collections.abc.Mapping` will define `__match_map__ = True`.
>      >
>      > Using this in Python would look like:
>      >
>      > ```
>      > class MySeq:
>      >      __match_seq__ = True
>      >      ...
>      >
>      > class MyMap:
>      >      __match_map__ = True
>      >      ...
>      > ```
 
[Mark, in response]
>     I don't like the way this need special inheritance rules, where
>     inheriting one attribute mutates the value of another.
>     It seems convoluted.
>
>     Consider:
>
>     class WhatIsIt(MySeq, MyMap):
>           pass
>
>     With __match_container__ it works as expected with no special
>     inheritance rules.
 
[me, responding to Mark]
> Wait a minute, do you expect WhatIsIt to be a sequence but not a map?
> *I* would expect that it is both, and that's exactly what Brandt's
> proposal does. So I see this as a plus.

[Now back to Mark]
Earlier you said:

     Classes that are both mappings and sequences are ill-conceived.
     Let's not compromise semantics or optimizability to support these.
    (IOW I agree with Mark here.)

Ah, you caught me there. I do think that classes that combine both characteristics are in troublesome water. I think we can get optimizability either way, so I'll focus on semantics.

Brandt has demonstrated that it's ugly to write the code for a class that in match statements behaves as either a sequence or a mapping (but not both) while at the same time keeping the code compatible with Python 3.9 or before. I also think that using flag attributes that are set to True or False (instead of using a bitmap of flags, which is obscure to many Python users) solves this problem nicely.

Using separate flag attributes happens to lead to different semantics than the flags-bitmap approach in the case of multiple inheritance. Given that one *can* inherit from both Sequence and Mapping, having separate flags seems slightly better than the flags-bitmap approach. It wasn't enough to convince me earlier, but the other advantage does convince me: separate flag attributes are better than using a flags-bitmap.

Now, if it weren't for other issues, having no flags at all here but just signalling the applicable pattern kinds through inheritance from collections.abc.{Sequence,Mapping} would be even cleaner. But we do have other issues: (a) the exceptions for str, bytes, bytearray, and (b) the clumsiness of importing collections.abc (which is Python code) deep in the ceval main loop. So some explicit form of signalling this is fine -- and for classes that explicitly inherit from Sequence or Mapping will get it for free that way.
 
PEP 653 requires that:
(__match_container__ & (MATCH_SEQUENCE | MATCH_MAPPING)) !=
(MATCH_SEQUENCE | MATCH_MAPPING)

Would you require that (__match_seq__ and __match_map__) is always false?

Nope.

If so, then what is the mechanism for handling the `WhatIsIt` class?
If not, then you loose the ability to make a single test to determine
which patterns can apply.

Translating the flag attributes to bits in tp_flags (or in a new flags variable elsewhere in the type object) would still allow a pretty fast test. And needing to support overlapping subsets of the cases is not unique to this situation, after all a class may well be a sequence *and* have attributes named x, y and z.
 
>     I think we are close to agreement on the mechanism for selecting which
>     pattern to match, but I still want the better defined semantics of
>     PEP 653.
>
>
> I don't know that PEP 653's semantics are better. Have you analyzed any
> *differences* besides the proposal above? I've personally found reading
> your pseudo-code very difficult, so I simply don't know.

PEP 653 semantics are more precise. I think that is better :)

I wish I knew of a single instance where PEP 634 and PEP 653 actually differ.
 
Apart from that, I think the semantics are so similar once you've added
__match_seq__/__match_map__  to PEP 634 that is hard to
claim one is better than the other.
My (unfinished) implementation of PEP 653 makes almost no changes to the
test suite.

I'd like to see where those differences are -- then we can talk about which is better. :-)
 
The code in the examples is Python, not pseudo-code.
That might be easier to follow.

--
--Guido van Rossum (python.org/~guido)