Hi Mark,
Thank you for your proposal to try and have more precise semantics for pattern matching. Of course, the proposal primarily introduces a new and extended protocol for pattern matching, upon which the 'semantics' is then based. I think it is important to recognise and discuss your work on this basis that it proposes a different protocol of how pattern matching works, before we consier whether your semantics really is 'more precise'.
As I understand it, your PEP proposes two major differences: control over the matching process moves from patterns to the objects and a custom protocol is introduced (via the `__deconstruct__` method). There is also the matter of the `__match_kind__` with the intention of improving performance, which I find of minor importance here.
If you go back and look at the history and development of PEP 634, you will find that we started off with a fully customisable method that we called `__match__` instead of `__deconstruct__`. The idea of more flexible and customisable pattern matching is thus certainly in the spirit of our proposal as well. However, we finally removed this part from our proposal due to strong concerns from the community that the match protocol is too complex, particularly for an initial version of pattern matching. Should the need arise, it would still be possible to later add such a customisation protocol in an upcoming version of Python.
Given these concerns with respect to complexity and our concession to remove the `__match__` method, I am wondering: do you have strong arguments that would speak for inclusion of this feature in the first version of pattern matching after all? I would love to have this feature and am truly interested in any arguments in its favour.
When pattern matching meets OOP, there is indeed some discussion that can be had on where the matching itself is to happen. Simply put: should an object recognise that it is an instance of a given class, or should the class recognise that an object is an instance of it?
In the spirit of Python's `instancecheck` method, we opted for classes/patterns recognising whether an object matches, where the object itself is 'passive'. In other words, `case C()` would match any object that is an instance of a subclass of `C`, in line with usual OOP principles (where any instance of a subclass of `C` is still a valid instance of `C` itself).
Your design turns this around and has the object take on the responsibility of checking whether it wants to match a specific pattern. This has the advantage that the object itself can morph into anything it wants to—as long as it is aware of the pattern. And therein lies the problem that IMHO fully negates one of the major advantages of pattern matching: that the patterns and the objects that are to be matched are de-coupled and fully independent. In other words: in your design it is not she who writes the patterns that controls what objects match, but he who designs the class hierarchy of the objects used.
One of the great traits of pattern matching is that it works out-of-the-box even with objects from ancient or alien code bases. Pattern matching according to PEP 634 is isolated, it happens only where pattern matching is explicitly used. When writing classes, you do not have to worry about pattern matching at all, unless you want your classes to be used as patterns. And should we decide some time in the future that we want to introduce new kinds of patterns, we still do not have to change a single object because the matching happens in the patterns and not in the objects, anyway. That his would not be the case with your design is already obvious with the list of `__match_kind__` for common builtin classes, for instance.
Although there are some other programming languages that chose to follow this route, I think it goes against the spirit of what is already there in Python, it violates the principle of separated concerns and is thus a rather ill-advised modification of the original protocol.
Kind regards,
Tobias
Hi Oscar,
Quoting Oscar Benjamin <oscar.j....@gmail.com>:
> Yes, thanks Mark. I'm not sure I've fully understood the PEP yet but I can see some parts that I definitely like. [...]
As I have just noted in my response to Mark, the aspect with the "deconstructor" (or `__match__` protocol as we called it) is definitely something that I like, too. Moreover, I think that packages like sympy might make a strong argument for it.
That being said, perhaps we would have to start thinking about concrete use cases first and then consider how to best provide for those with an extended match protocol. Even though I still like the idea of a flexible match method, there might be aspects that I/we overlook without knowing exactly where we want to go with this.
> I'm not entirely sure but I think that with PEP 653 you can implement this like:
>
> def __deconstruct__(obj):
> if obj.step != 1:
> return obj.start, obj.stop, obj.step
> elif obj.start != 0:
> return obj.start, obj.stop
> else:
> return obj.stop
>
> I think that would then mean you could use a pattern range(10) to unambiguously match range(10) without matching range(10, 20) etc.
This is certainly more of an anti-pattern (no pun intended) than an argument for the match protocol. Shuffling positional arguments around like this seems like a rather bad idea, particularly in the context of pattern matching. I'd rather write `case range(_, stop, _)` or `case range(0, stop, 1)` and be explicit here.
Kind regards,
Tobias
Hi Mark,
Quoting Mark Shannon <ma...@hotpy.org>:
[...]
If you had published these "more complicated, powerful protocols", you might be able to claim that this is a "rehash".
But you didn't.
I would say that these ideas have been quite prominently published:
https://www.python.org/dev/peps/pep-0622/#custom-matching-protocol
https://dl.acm.org/doi/10.1145/3426422.3426983
Moreover, much of the discussion is publicly available, for instance here:
https://github.com/gvanrossum/patma/issues/8
Cheers,
Tobias
Hi Mark,
Quoting Mark Shannon <ma...@hotpy.org>:
Hi Tobias,
[...]
But they are not referenced in PEP 634. I shouldn't have to trawl the internet to find the rejected ideas section.
That paper describes a `__match__` method, which is absent from PEP 634.
Why?
Cheers,
Mark.
You are right: you should not have to trawl the internet to get these information. However, given the history of the Pattern Matching PEPs and that PEP 622 is linked directly from PEP 634, I would think it is not that far a journey—even though there have been so many discussions that it has definitely become unwieldy to retain an overview...
Anyway, the answer to both your questions lies in that the Pattern Matching feature itself is rather complex and the PEPs ended up being huge and hard to read and understand as it is. We therefore refrained from long lists of rejected ideas in PEP 634, since that has already been done in PEP 622. Moreover, the `__match__` method and protocol were removed from the PEPs to focus on a core infrastructure and keep it as simple as possible.
I think such a customisable protocol will eventually add great value to pattern matching. However, one of the main arguments was to wait until we have more experience with pattern matching in Python and can give specific use cases for this extended protocol, which allow us to guide the design of the protocol.
At the end of the day, I guess we quite agree that we would like to have `__match__`, `__deconstruct__` or whatever you want to name it. But the slight variations in design demonstrate that it might also be a good idea to carefully lay it out first, before adding it to the pattern matching engine. Hence, separating this from the core implementation seems like a good idea that I still fully support (despite also looking forward to having it some day in the future ^_^).
Cheers,
Tobias
Hi Oscar
Quoting Oscar Benjamin <oscar.j....@gmail.com>:
On Fri, 19 Feb 2021 at 15:41, Tobias Kohn <ko...@tobiaskohn.ch> wrote: [...]
It's not easy now to look back over the history of all of this. My
recollection of the original version of PEP 622 (if that's the right
one...) was that it had an overcomplicated protocol for __match__. It
needed to be simplified but in the end it was dropped.
Without further context, I would agree with you: it is difficult to look back over the entire history of the pattern matching PEPs. However, on the one hand PEP 622 is directly linked in the current PEP 634. On the other hand, for those who actively particiated in the discussion of both PEP 622 as well as the DLS paper, I find it a bit too easy and 'convenient' to call those resources now 'hard to find'.
That being said, I think it is part of your homework to first research about the history and what others have done before proposing your innovation. Granted, this is hard and very laborious work that often takes a long time and can be frustrating. But if we want to make progress and move forward, we have to stop running in circles.—To be absolutely clear here: I do not think that Mark's proposal is running in circles and I think it is fair enough to bring up these ideas. But I equally think it is well possible to acknowledge that one part of it is a discussion of existing ideas, and to have a look at why these ideas have not made it into PEP 634.
The question now is if it will be straight-forward to retrofit a
protocol to PEP 634 after it is released and when backward
compatibility constraints kick in. PEP 653 (as discussed here) is
precisely an attempt to retrofit a protocol to PEP 634. I think the
difficulties involved in achieving that will become harder rather than
easier in future.
--
Oscar
There are actually two protocols in PEP 653. One is about changing the fundamental design of pattern matching as outlined in PEP 634. This is a part that I reject for reasons presented in one of my last posts. The second one is the customisation part using `__deconstruct__` or `__match__`. This is something that I like a lot and would certainly like to see in pattern matching—alas, just being in favour of something is not a strong enough argument for it.
Similar to my votum above: if we are going to discuss this, I think we need new arguments or evidence, something to show why the previous decision to drop it (for now) was wrong. For that it might be worth reading the respective section in deferred ideas of PEP 622. Some part of our decision was based on the notion that adding such a custom protocol later one will be relatively painless, but if we were wrong, please speak up and show us what we have overlooked. If you can present, for instance, some real-world use cases where this feature would enormously benefit sympy (or any other popular package), that would be helpful and a good starting point.
Kind regards,
Tobias