On Thu, 16 Jul 2020 at 02:09, Guido van Rossum <gu...@python.org> wrote:
>
> On Wed, Jul 15, 2020 at 4:41 PM Oscar Benjamin <oscar.j.benja...@gmail.com> 
> wrote:
>>
>> I've taken a look through PEP 622 and I've been thinking about how it
>> could be used with sympy.
>>
>> In principle case/match and destructuring should be useful for sympy
>> because sympy has a class Basic which defines a common structure for
>> ~1000 subclasses. There are a lot of places where it is necessary to
>> dispatch on the type of some object including in places that are
>> performance sensitive so those would seem like good candidates for
>> case/match. However the PEP doesn't quite seem as I hoped because it
>> only handles positional arguments indirectly and it does not seem to
>> directly handle types with variadic positional args.
>>
[snip]
>>
>> From a first glimpse of the proposal I thought I could do matches like this:
>>
>> match obj:
>>     case Add(Mul(x, y), Mul(z, t)) if y == t:
>>     case Add(*terms):
>>     case Mul(coeff, *factors):
>>     case And(Or(A, B), Or(C, D)) if B == D:
>>     case Union(Interval(x1, y1), Interval(x2, y2)) if y1 == x2:
>>     case Union(Interval(x, y), FiniteSet(*p)) | Union(FiniteSet(*p), 
>> Interval(x, y)):
>>     case Union(*sets):
>>
>> Knowing the sympy codebase each of those patterns would look quite
>> natural because they resemble the constructors for the corresponding
>> objects (as intended in the PEP). It seems instead that many of these
>> constructors would need to have args= so it becomes:
>>
>> match obj:
>>     case Add(args=(Mul(args=(x, y)), Mul(args=(z, t)))) if y == t:
>>     case Add(args=terms):
>>     case Mul(args=(coeff, *factors)):
>>     case And(args=(Or(args=(A, B)), Or(args=(C, D)))) if C == D:
>>     case Union(args=(Interval(x1, y1), Interval(x2, y2))) if y1 == x2:
>>     case Union(args=(Interval(x, y), FiniteSet(args=p))) | 
>> Union(args=(FiniteSet(args=p), Interval(x, y))):
>>     case Union(args=sets):
>>
>> Each of these looks less natural as they don't match the constructors
>> and the syntax gets messier with nesting.
>
>
> That's a really interesting new use case you're bringing up.
>
> You may have noticed that between v1 and v2 of the PEP we withdrew the 
> `__match__` protocol; we've been brainstorming about different forms a future 
> `__match__` protocol could take, once we have more practical experience. One 
> possible variant we've been looking at would be something that would *only* 
> be used for positional arguments -- `__match__` would just return a tuple of 
> values extracted from the object that can then be matched by the 
> interpreter's match machinery. Your use case could then (almost, see below) 
> be handled by having `__match__` just return `self.args`.

That would work but something else just occurred to me which is that
as I understand it the intention of __match_args__ is that it is
supposed to correspond to the parameter list for __init__/__new__ like

class Thing2:
    __match_args__ = ('first', 'second')

    def __init__(self, first, second):
        self.first = first
        self.second = second

That is deliberate so that matching can have a similar logic to the
way that arguments are handled when calling __init__:

match obj:
    case Thing2(1, 2):
    case Thing2(1, second=2):
    case Thing2(first=1, second=2):
    ...

Maybe __match_args__ could have a way to specify the variadic part of
a parameter list as well:

class ThingN:
    __match_args__ = ('first', 'second', '*rest')

    def __init__(self, first, second, *rest):
        self.first = first
        self.second = second
        self.rest = rest

Then you can match with

match obj:
    case ThingN(1, 2):
    case ThingN(1, 2, 3):
    case ThingN(first, second, *rest):
    case ThingN(first, *second_and_rest):
    case ThingN(*allargs):
    ...

The normal restrictions for combinations of keyword and positional
arguments could apply to the patterns as if __match_args__ was the
parameter list for a function.

Perhaps the * in '*rest' isn't as clear between quotes and some more
noticeable syntax could be used like

    __match_args__ = ('first', 'second', ('rest',))


--
Oscar
_______________________________________________
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/SMN5GZHGURK2AQKTBES5Z6XLWPDUBNB6/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to