with the addition of PEP 634 and the new match/case syntax in python 3.10, it seems fitting to be able to use these new match statements inside of expressions as well, which is why I think we should have a new `matches` keyword (similar to if/else, is, for, not, etc.) for use in expressions. This makes many more complex if/else statements much less complex (see example use cases at #2), and overall looks much more pythonic in practice. The expression would be higher on the order of operations than the boolean operators, but higher than the comparisons, but this is subject to change. (see #1) However, this should be used sparingly (ideally for single comparisons), similar to list comprehensions versus for loops.
1. GRAMMAR MODIFICATIONS: ``` inversion: | 'not' inversion | match_expression
matches_expression: | comparison 'matches' patterns # patterns is defined in PEP 634 | comparison ```
2. USE CASES: a. ``` if variable matches ["example", *files]: print(files) ``` as opposed to: ``` match variable: case ["example", *files]: print(files) ``` b. ``` for x in some_list: if x matches Class1(attr1=3) | Class2(attr2=4): print("got a match!") break ``` as opposed to ``` for x in some_list: match x: case Class1(attr1=3) | Class2(attr2=4): print("got a match!") break ```
There was a discussion about this a couple months ago but instead of adding a new keyword the idea was that the walrus operator could be upgraded from simply being a binding operator to matching patterns supported by the match statement.
if ["example", *files] := variable: print(files)
If the pattern doesn't match the expression would evaluate to None instead of the right hand side value. The current syntax would still work the same as it would be interpreted as an unconditional name binding pattern.
foo = [1, 2, 3] assert ( := foo) is None assert ([a, b, c] := foo) is foo assert (bar := foo) is foo # No special case needed for the current behavior
ehhh in my opinion its more pythonic to use a keyword in this case, especially since that seems to be the main way it is currently done with statements being turned into expressions
On the other hand I think extending the walrus operator would make the change less intrusive and the syntax more easily discoverable:
if match := re.match(r"...", some_string): print(match, match)
if [_, a, b] := re.match(r"...", some_string): print(a, b) # Assuming match objects would then be made into proper sequences
Also, since it's not a new operator there's no need to worry about operator precedence. And since the current behavior of the walrus operator would already implement a subset of the proposed changes, I think it would reduce friction when deciding whether or not to adopt it.
One last point is that the new operator would lead to two equivalent constructs:
if thing.value matches foo: ...
if foo := thing.value: ...
I think using the "matches" operator like this would probably be frowned upon but that's yet another thing you would need to explain when teaching the language. Also, I can easily imagine some codestyle where the walrus operator would be considered obsolete as it would overlap with the "matches" operator while only providing a subset of the functionality, thus leading to fragmentation in the ecosystem.
On Mon, May 24, 2021 at 07:55:07PM -0000, Tyler F wrote:
with the addition of PEP 634 and the new match/case syntax in python 3.10, it seems fitting to be able to use these new match statements inside of expressions as well,
Python 3.10 isn't even showing up on the Downloads page yet:
It is still in beta, the first production-ready version is not due out until October.
So how much real-world practical experience do you have now with the match/case syntax? Do you think that the community overall has as much experience with this syntax as you?
Normally we have years of practical experience with a feature before being able to judge its true worth and ability to extend it to new features. It was many years between if statements and the if operator; many years between for loops and comprehensions. We still don't have a try...except expression.
However, this should be used sparingly (ideally for single comparisons), similar to list comprehensions versus for loops.
In my experience, list comprehensions are used more than for loops now. But even if they aren't used literally more often than for loops, we can hardly say that comprehensions are used *sparingly*. They are an extremely popular and common feature.
So what does this matches operator return? You have this example:
if variable matches ["example", *files]: print(files)
but we can extract the matches expression outside of the if statement.
(It's an expression, so we must be able to extract it out of the statement and use it alone, like any other expression.)
variable matches ["example", *files]
I can't interpret what that should do or what it would return. Suppose I assigned it to a variable, or printed it:
spam = variable matches ["example", *files] print(variable matches ["example", *files])
What's the value of `spam` (or what gets printed)?
If we can use it in an if statement, it has to return a value that can be either truthy or falsey, possibly even True or False itself.
How does that reconcile with the multi-branching match statement? A match statement can take an arbitrarily large number of cases, and each case is a block. How do you get the same effect in an expression?
Match statements also have binding side-effects. Binding in Python is *almost* entirely done with statements:
spam = eggs # assignment import spam for spam in iterable: ... except Exception as err: ...
etc. The only expression that performs binding is the walrus operator, and that is still new and remains controversial. This would be a second one.