patma icon indicating copy to clipboard operation
patma copied to clipboard

A utilities module for pattern matching

Open ilevkivskyi opened this issue 4 years ago • 7 comments

This discussion was started in #8, but now that issue is mostly about __match__() API. So I would like to open a separate issue about providing some utilities/helpers for pattern matching. There are two questions:

  • Should we add a new stdlib module or add to existing one?
  • What should go into that module?

For the first one I think it is better to have a new module, I don't see a good fit among the existing modules. Fo the second, I think we should probably add a helper wrapper class for constructing match proxies, something like MatchWrapper, the class would use a __getattr__() and allow hiding/adding attributes for match purpose, setting __match_args__, and maybe something else. For example:

from patterns import MatchWrapper

class Point:
    def __init__(self, x: int, y: int) -> None:
        self.x = x
        self.y = y
        self._processed = False

    @classmethod
    def __match__(cls, obj):
        if isinstance(obj, cls):
            return MatchWrapper(obj, ['x', 'y'], coordinates=[obj.x, obj.y])

p = Point(1, 2)
match p:
    as Point(x, y):  # Works
        ...
    as Point(coordinates=[1, 2]):  # Also works
        ...
    as Point(_processed=False):  # Not included in allowed attributes, raises
        ...

Also it would be useful to have some custom patterns (as initially proposed in #8). I would like to have these two there Len(x) and InRange(x, y). The latter may be tricky using current "pattern-agnostic" API.

I would propose to gather more ideas about possible predefined patterns here.

ilevkivskyi avatar May 16 '20 10:05 ilevkivskyi

Just for fun here is a possible implementation of InRange(x, y):

class InRange:
    @staticmethod
    def __match__(obj):
        if isinstance(obj, (int, float)):
            return MatchWrapper(low=DummyLow(obj), high=DummyHigh(obj))

class DummyLow:
    def __init__(self, value):
        self.value = value
    def __eq__(self, low):
        if not isinstance(low, (int, float)):
            raise ImpossibleMatch("InRange() requires numbers")
        return self.value >= low

class DummyHigh:
    def __init__(self, value):
        self.value = value
    def __eq__(self, high):
        if not isinstance(high, (int, float)):
            raise ImpossibleMatch("InRange() requires numbers")
        return self.value < high

I think this should work, but the problem is that if one matches against InRange(x, y) with names, then interpreter will just assign these crazy objects to x and y.

UPDATE: simplified implementation using MatchWrapper.

ilevkivskyi avatar May 16 '20 10:05 ilevkivskyi

I am not so keen on such magic objects any more. Even Len() seems better expressed using a guard. I worry that people would have to learn a bunch of new pseudo functions for stuff that they already know how to spell using == or < etc. And somehow these don’t strike me as great use cases for pattern matching, nor common.

ADDED: But +1 on a module with helpers like MatchWrapper.

gvanrossum avatar May 16 '20 15:05 gvanrossum

I am not so keen on such magic objects any more. [...] But +1 on a module with helpers like MatchWrapper.

OK, I agree.

ilevkivskyi avatar May 17 '20 06:05 ilevkivskyi

All those fancy applications are kind of interesting to show the power of pattern matching, i.e. that you can basically express almost anything. But for real-world use, I would also rather not include them or provide them in a module. As for ranges, Python has IMHO one of the most elegant ways of expressing that with a <= x <= b—which is something I often direly miss in other languages.

But I, too, think that the module with the MatchWrapper (and possibly other things) is a great idea.

Tobias-Kohn avatar May 17 '20 14:05 Tobias-Kohn

The PEP should move the patterns module to the section Deferred Ideas.

gvanrossum avatar Jun 03 '20 03:06 gvanrossum

I really believe this is fully pepped. @brandtbucher can you remember why you marked is as needs more pep?

gvanrossum avatar Jul 02 '20 05:07 gvanrossum

Nope.

brandtbucher avatar Jul 02 '20 05:07 brandtbucher