[Python-Dev] PEP 642 v3: Explicit patterns for structured pattern matching
I’ve made a final round of updates to PEP 642 and submitted it to the Steering Council for consideration alongside PEP 634. As usual, the rendered version can be found here: https://www.python.org/dev/peps/pep-0642/ There's a Discourse thread at https://discuss.python.org/t/pep-642-v3-explicit-pattern-syntax-for-structural-pattern-matching/6459, and the rest of the email covers the same points as the opening post in that thread. There are some pretty significant changes relative to v2, although I did already discuss most of them in the v2 thread at https://discuss.python.org/t/pep-642-constraint-pattern-syntax-for-structural-pattern-matching/5614 The PEP itself contains a list of major changes relative to PEP 634, so I won’t repeat that here: https://www.python.org/dev/peps/pep-0642/#appendix-c-summary-of-changes-relative-to-pep-634 Instead I’ll summarise the parts that I consider most important: * ensuring that all “binding to the right” operations use the as keyword. This drove changes to both mapping patterns and class patterns. * explicitly qualifying both name bindings and value constraints with `as`, `==`, or `is`. This change makes it possible to make pattern matching available to users without having to resolve the thorny questions of what bare names and attribute references should do by default. It also opens up the possibility of potentially adding more value constraint options later (like `in` , `is not`, and `!=`) if those operations seem sufficiently compelling to be worth adding. * explicitly decoupling sequence pattern matching from iterable unpacking. The change to require qualification of name binding operations already breaks the alignment between the two, and that created an opportunity to simplify the grammar by only allowing square bracket based sequence patterns and eliminating both open sequence patterns and parenthesis based sequence patterns * changing class patterns to draw more of their syntactic inspiration from mapping patterns rather than from class instantiation * explicitly representing patterns in the AST, rather than treating patterns as pseudo-expressions all the way through to the code generation layer. Skipping this step makes the code fragile and hard to follow, as there isn’t actually any point in the AST that accepts both expressions and patterns, but with pattern parsing reusing expression nodes, you can’t tell from just looking at the AST which nodes expect subexpressions and which expect subpatterns. I’ll also quote the example match statement from the PEP abstract, which extracts “host” and “port” details from a 2 item sequence, a mapping with “host” and “port” keys, any object with “host” and “port” attributes, or a “host:port” string, treating the “port” as optional in the latter three cases: port = DEFAULT_PORT match expr: case [as host, as port]: pass case {"host" as host, "port" as port}: pass case {"host" as host}: pass case object{.host as host, .port as port}: pass case object{.host as host}: pass case str{} as addr: host, __, optional_port = addr.partition(":") if optional_port: port = optional_port case __ as m: raise TypeError(f"Unknown address format: {m!r:.200}") port = int(port) Cheers, Nick. -- Nick Coghlan | ncogh...@gmail.com | Brisbane, Australia ___ 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/GQHKW5KHXWZ3Y2E2KOJ72GT3IRGGEEUE/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: PEP 642 v3: Explicit patterns for structured pattern matching
> On 3 Jan 2021, at 15:21, Nick Coghlan wrote: > > I’ve made a final round of updates to PEP 642 and submitted it to the > Steering Council for consideration alongside PEP 634. > > As usual, the rendered version can be found here: > https://www.python.org/dev/peps/pep-0642/ > > There's a Discourse thread at > https://discuss.python.org/t/pep-642-v3-explicit-pattern-syntax-for-structural-pattern-matching/6459, > and the rest of the email covers the same points as the opening post > in that thread. > > There are some pretty significant changes relative to v2, although I > did already discuss most of them in the v2 thread at > https://discuss.python.org/t/pep-642-constraint-pattern-syntax-for-structural-pattern-matching/5614 > > The PEP itself contains a list of major changes relative to PEP 634, > so I won’t repeat that here: > https://www.python.org/dev/peps/pep-0642/#appendix-c-summary-of-changes-relative-to-pep-634 > > Instead I’ll summarise the parts that I consider most important: > > * ensuring that all “binding to the right” operations use the as > keyword. This drove changes to both mapping patterns and class > patterns. > * explicitly qualifying both name bindings and value constraints with > `as`, `==`, or `is`. This change makes it possible to make pattern > matching available to users without having to resolve the thorny > questions of what bare names and attribute references should do by > default. It also opens up the possibility of potentially adding more > value constraint options later (like `in` , `is not`, and `!=`) if > those operations seem sufficiently compelling to be worth adding. > * explicitly decoupling sequence pattern matching from iterable > unpacking. The change to require qualification of name binding > operations already breaks the alignment between the two, and that > created an opportunity to simplify the grammar by only allowing square > bracket based sequence patterns and eliminating both open sequence > patterns and parenthesis based sequence patterns > * changing class patterns to draw more of their syntactic inspiration > from mapping patterns rather than from class instantiation > * explicitly representing patterns in the AST, rather than treating > patterns as pseudo-expressions all the way through to the code > generation layer. Skipping this step makes the code fragile and hard > to follow, as there isn’t actually any point in the AST that accepts > both expressions and patterns, but with pattern parsing reusing > expression nodes, you can’t tell from just looking at the AST which > nodes expect subexpressions and which expect subpatterns. > > I’ll also quote the example match statement from the PEP abstract, > which extracts “host” and “port” details from a 2 item sequence, a > mapping with “host” and “port” keys, any object with “host” and “port” > attributes, or a “host:port” string, treating the “port” as optional > in the latter three cases: > >port = DEFAULT_PORT >match expr: >case [as host, as port]: >pass >case {"host" as host, "port" as port}: >pass >case {"host" as host}: >pass >case object{.host as host, .port as port}: >pass >case object{.host as host}: >pass >case str{} as addr: >host, __, optional_port = addr.partition(":") >if optional_port: >port = optional_port >case __ as m: >raise TypeError(f"Unknown address format: {m!r:.200}") >port = int(port) I read the above and believe I know what it meant without needing to read the PEP in detail. I like that a lot. I quickly read 642 v3 and missed an explanation about why the syntax to match a string object is str{} and not str. Are you saying that I MUST use {} so that when case is parsed its clear that its a class with no constraints? in the "Changes to class patterns" I read the BinaryOp example and I thought from the above that it would also use {} and not (). --- match expr: case BinaryOp(== '+', as left, as right): --- I was expecting to see: --- match expr: case BinaryOp{== '+', as left, as right}: --- Barry > > Cheers, > Nick. > > -- > Nick Coghlan | ncogh...@gmail.com | Brisbane, Australia > ___ > 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/GQHKW5KHXWZ3Y2E2KOJ72GT3IRGGEEUE/ > Code of Conduct: http://python.org/psf/codeofconduct/ ___ 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@pyt
[Python-Dev] Re: PEP 642 v3: Explicit patterns for structured pattern matching
On Sun, 3 Jan 2021 16:19:01 + Barry Scott wrote: > > > > I’ll also quote the example match statement from the PEP abstract, > > which extracts “host” and “port” details from a 2 item sequence, a > > mapping with “host” and “port” keys, any object with “host” and “port” > > attributes, or a “host:port” string, treating the “port” as optional > > in the latter three cases: > > > >port = DEFAULT_PORT > >match expr: > >case [as host, as port]: > >pass > >case {"host" as host, "port" as port}: > >pass > >case {"host" as host}: > >pass > >case object{.host as host, .port as port}: > >pass > >case object{.host as host}: > >pass > >case str{} as addr: > >host, __, optional_port = addr.partition(":") > >if optional_port: > >port = optional_port > >case __ as m: > >raise TypeError(f"Unknown address format: {m!r:.200}") > >port = int(port) > > I read the above and believe I know what it meant without needing to read the > PEP in detail. > I like that a lot. +1. Unlike the other PEP, there is no confusion with regular Python syntax such as function calls. Regards Antoine. ___ 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/23DGXSXJ32DMJMZIETSEWGYDX2RNIR3W/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: PEP 642 v3: Explicit patterns for structured pattern matching
On Sun, 3 Jan 2021 at 16:26, Barry Scott wrote: > I read the above and believe I know what it meant without needing to read the > PEP in detail. > I like that a lot. Personally, I read it and was horribly confused. I worked out most of it, but I would *not* count it as intuitive or natural. Specific examples: case {"host" as host, "port" as port}: pass case {"host" as host}: pass I assume that's dictionary unpacking? It doesn't really look like anything else in Python, though, and it took me a while to work out. case object{.host as host, .port as port}: pass I can only guess at this. I assume "subclass of object with host and port attributes/properties"? But why use {...} for object access? And does that extend to Foo{...} meaning "subclass of Foo? There's no reason to assume yes or no to that. Overall, the whole thing feels like an attempt to invent some sort of syntax in reaction to the PEP 634 form - not having a logic in its own right, but simply with a driving principle of "don't be like PEP 534". It abandons any idea of "make matching look like the thing that's being matched" and replaces it with a completely new set of syntax, which lacks the intuitive nature that I expect from Python (and yes, I know that can be read as "I'm not familiar with it, so I don't like it" - that may indeed be all that it is, but I feel that it's a bit more fundamental than just that). I have not read the full PEP, so take this as very much a "first impression" reaction, but I'd avoid using this syntax, both in my own projects and in any project I contribute to. Honestly, I feel like I'd rather see pattern matching rejected altogether than see this version accepted. Paul ___ 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/GFNGO5UZB67HKWN3WSKGMFYEPE3RAFMK/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: PEP 642 v3: Explicit patterns for structured pattern matching
On 1/3/21 8:50 AM, Paul Moore wrote: Personally, I read it and was horribly confused. case object{.host as host, .port as port}: pass Leading periods is a big no-go for me, for all the reasons listed in the original thread. I have not read the full PEP, so take this as very much a "first impression" reaction, but I'd avoid using this syntax, both in my own projects and in any project I contribute to. Honestly, I feel like I'd rather see pattern matching rejected altogether than see this version accepted. Agreed. -- ~Ethan~ ___ 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/SO4K2WNUZDKIPYJOPVTBNXOWEBIOQWYP/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: PEP 642 v3: Explicit patterns for structured pattern matching
Hello, On Sun, 3 Jan 2021 16:50:33 + Paul Moore wrote: > On Sun, 3 Jan 2021 at 16:26, Barry Scott > wrote: > > I read the above and believe I know what it meant without needing > > to read the PEP in detail. I like that a lot. > > Personally, I read it and was horribly confused. I worked out most of > it, but I would *not* count it as intuitive or natural. > > Specific examples: > > case {"host" as host, "port" as port}: > pass > case {"host" as host}: > pass > > I assume that's dictionary unpacking? It doesn't really look like > anything else in Python, though, and it took me a while to work out. > > case object{.host as host, .port as port}: > pass > > I can only guess at this. I assume "subclass of object with host and > port attributes/properties"? But why use {...} for object access? And > does that extend to Foo{...} meaning "subclass of Foo? There's no > reason to assume yes or no to that. > > Overall, the whole thing feels like an attempt to invent some sort of > syntax in reaction to the PEP 634 form - not having a logic in its own > right, but simply with a driving principle of "don't be like PEP 534". > It abandons any idea of "make matching look like the thing that's > being matched" and replaces it with a completely new set of syntax, > which lacks the intuitive nature that I expect from Python +1 for that evaluation. And I'd like to remind with what the original discussion started, with very simple and focused questions: a) What if we *do* use sigils for value patterns? or, b) What if we *do* use sigils for capture patterns? Instead of focusing on one of the above questions, making fine, focused, pin-head sized adjustments to the original pattern matching PEPs, with PEP642 we've got a noticeable scope creep, with evermore extravagant syntax replacements of the large part of the original proposal. This once again shows that it's very easy to get carried away and how important for others to help to remind where to stop. [] > I have not read the full PEP, so take this as very much a "first > impression" reaction, but I'd avoid using this syntax, both in my own > projects and in any project I contribute to. Honestly, I feel like I'd > rather see pattern matching rejected altogether than see this version > accepted. Just the same, there's no need to go too far here either. The question of PEP642 rejection should be treated as orthogonal to acceptance / rejection of PEP634. Because, well, PEP634 misses to do some things better, but indeed at least some of them can be added later. But PEP642 twists matters so much, that it likely will be irreparable afterwards. > > Paul -- Best regards, Paul mailto:pmis...@gmail.com ___ 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/GBKFB54YFUY3Y6PC5BXYNMTD6NDEAZPG/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: PEP 642 v3: Explicit patterns for structured pattern matching
On Mon, 4 Jan 2021, 2:19 am Barry Scott, wrote: > > I quickly read 642 v3 and missed an explanation about why the syntax to > match a string object is > str{} and not str. Are you saying that I MUST use {} so that when case is > parsed its clear that its a class > with no constraints? > Yes. "str{}" would give a pure subclass check, "str()" would also be allowed for classes that define "__match_args__". > in the "Changes to class patterns" I read the BinaryOp example and > I thought from the above that it would also use {} and not (). > > --- > > match expr: > > case BinaryOp(== '+', as left, as right): > > --- > > I was expecting to see: > > --- > > match expr: > > case BinaryOp{== '+', as left, as right}: > > --- > > The example in the abstract doesn't show a class defined pattern that relies on __match_args__, only an instance attributes pattern. Cheers, Nick. > ___ 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/D4ZSJUW4WFFWPWQWP23AAUSZ6AQXQT3M/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: PEP 642 v3: Explicit patterns for structured pattern matching
On Mon, 4 Jan 2021, 2:50 am Paul Moore, wrote: > On Sun, 3 Jan 2021 at 16:26, Barry Scott wrote: > > I read the above and believe I know what it meant without needing to > read the PEP in detail. > > I like that a lot. > > Personally, I read it and was horribly confused. I worked out most of > it, but I would *not* count it as intuitive or natural. > > Specific examples: > > case {"host" as host, "port" as port}: > pass > case {"host" as host}: > pass > > I assume that's dictionary unpacking? It doesn't really look like > anything else in Python, though, and it took me a while to work out. > These *can* have the colon included, and it would be quite viable to have that as the only spelling in the initial design iteration: case {"host": as host, "port": as port}: pass case {"host": as host}: pass The submitted grammar just allows the colon to be left out for brevity (as even with it missing you were still able to correctly identify this as a mapping match). > case object{.host as host, .port as port}: > pass > > I can only guess at this. I assume "subclass of object with host and > port attributes/properties"? Correct :) But why use {...} for object access? And > does that extend to Foo{...} meaning "subclass of Foo? There's no > reason to assume yes or no to that. > The instance attribute syntax arose from trying to deal with two problems from class patterns in PEP 634: * "ATTR=TARGET" using "=" to bind to the right instead of to the left * no subsequent path to ever offering a syntax for *retrieving* multiple attributes in ordinary expressions outside pattern matching The minimal fix for the first point would have been just "case object(host=as host, port=as port}:", but that couldn't ever be used to retrieve multiple attributes, as "object(host, port)" is already a function call. By contrast, "object{.host, .port}" is currently illegal syntax that could plausibly become a syntactic shorthand for "(object.host, object.port)" in both ordinary expressions and in assignment targets, even if it was initially only supported in pattern matching. > Overall, the whole thing feels like an attempt to invent some sort of > syntax in reaction to the PEP 634 form - not having a logic in its own > right, but simply with a driving principle of "don't be like PEP 534". > The design logic went as follows: 1. Start with PEP 634 2a. Ensure all "binding to the right" operations use 'as' 2b. Ensure all bare names outside subexpressions are qualified with a sigil or keyword 3. Ensure all deconstruction operations offer a potential future path to producing tuples in ordinary expressions using pattern matching inspired syntax It abandons any idea of "make matching look like the thing that's > being matched" PEP 634 doesn't offer that for class patterns either. I like being able to leave out the colon for mapping name bindings, but wouldn't object to requiring that it be left in. and replaces it with a completely new set of syntax, > which lacks the intuitive nature that I expect from Python (and yes, I > know that can be read as "I'm not familiar with it, so I don't like > it" - that may indeed be all that it is, but I feel that it's a bit > more fundamental than just that). > > I have not read the full PEP, so take this as very much a "first > impression" reaction, I'd definitely like to hear your second impression after reviewing the PEP text, but getting first impressions like yours was a big part of my motivation for including the match statement example. but I'd avoid using this syntax, both in my own > projects and in any project I contribute to. Honestly, I feel like I'd > rather see pattern matching rejected altogether than see this version > accepted. > I've reached that point with PEP 634 - I think the problems with binding to the right in mapping and especially class patterns are serious enough that I'd prefer to see no pattern matching to *that* syntax for pattern matching. I also have concerns about AST ambiguity in the current PEP 634 implementation, but those could be fixed by defining separate AST nodes for patterns without having to change the surface syntax proposal (the AST I've defined for 642 should be usable for 634 as well). Cheers, Nick. > Paul > ___ 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/VV3XFMBY4ZKGAOA4QJY3PGKQK4GUQRKS/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: PEP 642 v3: Explicit patterns for structured pattern matching
On Mon, 4 Jan 2021, 4:34 am Ethan Furman, wrote: > On 1/3/21 8:50 AM, Paul Moore wrote: > > > Personally, I read it and was horribly confused. > > >> case object{.host as host, .port as port}: > >> pass > > Leading periods is a big no-go for me, for all the reasons listed in the > original thread. > It gave me pause as well, but the differences I see relative to the proposed usage in the early iterations of PEP 622 are: 1. This dot isn't semantically significant to the parser, it's just a "this is an attribute name" hint for the human reader. If you forget it, you get a syntax error rather than the code meaning something else. 2. It only appears inside instance attribute mappings, not as part of arbitrary patterns, so the leading dot isn't the only "this is an attribute reference" hint. 3. It means the same thing as dots do in the rest of Python (indicating that the following identifier is an attribute name). Cheers, Nick. > ___ 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/KPDHJIU3MNU6ID6SVKGDGYW7AY435QYY/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Descriptors in dataclasses fields
I've been exploring dataclasses for a few months now and they've proven to be very useful. The only downside is that there's not a simple way to use descriptors. Descriptors only work on class attributes (as per the docs: https://docs.python.org/3/howto/descriptor.html#closing-thoughts). This means that to use a descriptor in a data class we have to use typing.ClassVar like this @dataclass class Item: name: str price: typing.ClassVar[Decimal] = PriceValidator() Which is totally fine because of how descriptors work the previous syntax is a feature in dataclasses, IMHO. But, there's not a straight forward way to pass a value to the descriptor on init. Because ClassVars are not used by @dataclass to do its thing (as per the docs: https://docs.python.org/3/library/dataclasses.html#class-variables) This means that in the example above `price` is not going to be a parameter of the class Item's __init__ method. So the only way to do this is to either create an InitVar field or a regular field and then pass the value to the descriptor in __post_init__ @dataclass class Item: name: str price_val: InitVar[Decimal] price: typing.ClassVar[Decimal] = PriceValidator() def __post_init__(self, price_val: Decimal) -> None: self.price = price_val When using a regular field we can double the field's purpose by making it the field the descriptor is going to use: @dataclass class Item: name: str _price: Decimal price: typing.ClassVar[Decimal] = PriceValidator() def __post_init__(self) -> None: self.price = self._price And then in the descriptor implement __set_name__ like so: def __set_name(self, owner, name): self.name = f"_{name}" Personally, I don't like either option because it adds noice to the data class definition. Using an InitVar is the better option because that variable is clearly defined as init only and it's not present in an instance. Using a regular field adds unnecessary noice to the data class. Also, I think it clashes with the intent of descriptors since they're supposed to manage their data in any way they want. My questions are: - Are my assumptions correct? - Is this the intended behavior? Or what's the preferred way of using a descriptor in a dataclass field? - If this is not intended, could it be possible to add a `descriptor` parameter to the `field` method and treat the field accordingly? I couldn't find any information on the docs or the PEP. I could"ve missed something, sorry if this is the case :) Thanks! -- Josue https://www.rmcomplexity.com ___ 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/UOMBDIVNRG3DS6UHWSOF4JTLIPXEENCT/ Code of Conduct: http://python.org/psf/codeofconduct/