One small question about this part of the PEP:

> For the most commonly-matched built-in types (bool, bytearray, bytes, dict,
float, frozenset, int, list, set, str, and tuple), a single positional
sub-pattern is allowed to be passed to the call. Rather than being matched
against any particular attribute on the subject, it is instead matched
against the subject itself.

Correct me if I'm wrong, but I don't think the PEP currently gives us a way
of enabling this behavior for classes not on this list. If so, would it be
worth adding a way?

It would help remove a special case and could come in handy when doing
things like creating my own custom data structures, for example. After all,
if `case dict(x)` makes x match the entire dict, it would be nice if I
could make `case MyCustomMapping(x)` behave in the same way to keep the
usage consistent.

We could maybe let classes opt-in to this behavior if they define
`__match_args__
= None`? Not sure if adding the extra "is None" check when doing the match
will introduce too much overhead though.

-- Michael


On Wed, Jul 8, 2020 at 8:06 AM Guido van Rossum <gu...@python.org> wrote:

> Today I’m happy (and a little trepidatious) to announce the next
> version of PEP 622, Pattern Matching. As authors we welcome Daniel F
> Moisset in our midst. Daniel wrote a lot of the new text in this
> version, which introduces the subject matter much more gently than the
> first version did. He also convinced us to drop the `__match__`
> protocol for now: the proposal stands quite well without that kind of
> extensibility, and postponing it will allow us to design it at a later
> time when we have more experience with how `match` is being used.
>
> That said, the new version does not differ dramatically in what we
> propose. Apart from dropping `__match__` we’re dropping the leading
> dot to mark named constants, without a replacement, and everything
> else looks like we’re digging in our heels. Why is that? Given the
> firestorm of feedback we received and the numerous proposals (still
> coming) for alternative syntax, it seems a bad tactic not to give up
> something more substantial in order to get this proposal passed. Let
> me explain.
>
> Language design is not like politics. It’s not like mathematics
> either, but I don’t think this situation is at all similar to
> negotiating a higher minimum wage in exchange for a lower pension,
> where you can definitely argue about exactly how much lower/higher
> you’re willing to go. So I don’t think it’s right to propose making
> the feature a little bit uglier just to get it accepted.
>
> Frankly, 90% of the issue is about what among the authors we’ve dubbed
> the “load/store” problem (although Tobias never tires to explain that
> the “load” part is really “load-and-compare”). There’s a considerable
> section devoted to this topic in the PEP, but I’d like to give it
> another try here.
>
> In case you’ve been avoiding python-dev lately, the problem is
> this. Pattern matching lets you capture values from the subject,
> similar to sequence unpacking, so that you can write for example
> ```
>     x = range(4)
>     match x:
>         case (a, b, *rest):
>             print(f"first={a}, second={b}, rest={rest}")  # 0, 1, [2, 3]
> ```
> Here the `case` line captures the contents of the subject `x` in three
> variables named `a`, `b` and `rest`. This is easy to understand by
> pretending that a pattern (i.e., what follows `case`) is like the LHS
> of an assignment.
>
> However, in order to make pattern matching more useful and versatile,
> the pattern matching syntax also allows using literals instead of
> capture variables. This is really handy when you want to distinguish
> different cases based on some value, for example
> ```
>     match t:
>         case ("rect", real, imag):
>             return complex(real, imag)
>         case ("polar", r, phi):
>             return complex(r * cos(phi), r * sin(phi))
> ```
> You might not even notice anything funny here if I didn’t point out
> that `"rect"` and `"polar"` are literals -- it’s really quite
> natural for patterns to support this once you think about it.
>
> The problem that everybody’s been concerned about is that Python
> programmers, like C programmers before them, aren’t too keen to have
> literals like this all over their code, and would rather give names to
> the literals, for example
> ```
>     USE_POLAR = "polar"
>     USE_RECT = "rect"
> ```
> Now we would like to be able to replace those literals with the
> corresponding names throughout our code and have everything work like
> before:
> ```
>     match t:
>         case (USE_RECT, real, imag):
>             return complex(real, imag)
>         case (USE_POLAR, r, phi):
>             return complex(r * cos(phi), r * sin(phi))
> ```
> Alas, the compiler doesn’t know that we want `USE_RECT` to be a
> constant value to be matched while we intend `real` and `imag` to be
> variables to be given the corresponding values captured from the
> subject. So various clever ways have been proposed to distinguish the
> two cases.
>
> This discussion is not new to the authors: before we ever published
> the first version of the PEP we vigorously debated this (it is Issue 1
> in our tracker!), and other languages before us have also had to come
> to grips with it. Even many statically compiled languages! The reason
> is that for reasons of usability it’s usually deemed important that
> their equivalent of `case` auto-declare the captured variables, and
> variable declarations may hide (override) like-named variables in
> outer scopes.
>
> Scala, for example, uses several different rules: first, capture
> variable names must start with a lowercase letter (so it would
> handle the above example as intended); next, capture variables
> cannot be dotted names (like `mod.var`); finally, you can enclose any
> variable in backticks to force the compiler to see it as a load
> instead of a store. Elixir uses another form of markup for loads: `x`
> is a capture variable, but `^x` loads and compares the value of `x`.
>
> There are a number of dead ends when looking for a solution that works
> for Python. Checking at runtime whether a name is defined or not is
> one of these: there are numerous reasons why this could be confusing,
> not the least of which being that the `match` may be executed in a
> loop and the variable may already be bound by a previous
> iteration. (True, this has to do with the scope we’ve adopted for
> capture variables. But believe me, giving each case clause its own
> scope is quite horrible by itself, and there are other
> action-at-a-distance effects that are equally bad.)
>
> It’s been proposed to explicitly state the names of the variables
> bound in a header of the `match` statement; but this doesn’t scale
> when the number of cases becomes larger, and requires users to do
> bookkeeping the compiler should be able to do. We’re really looking
> for a solution that tells you when you’re looking at an individual
> `case` which variables are captured and which are used for
> load-and-compare.
>
> Marking up the capture variables with some sigil (e.g. `$x` or `x?`)
> or other markup (e.g. backticks or `<x>`) makes this common case ugly
> and inconsistent: it’s unpleasant to see for example
> ```
>     case %x, %y:
>         print(x, y)
> ```
> No other language we’ve surveyed uses special markup for capture
> variables; some use special markup for load-and-compare, so we’ve
> explored this. In fact, in version 1 of the PEP our long-debated
> solution was to use a leading dot. This was however boohed off the
> field, so for version 2 we reconsidered. In the end nothing struck our
> fancy (if `.x` is unacceptable, it’s unclear why `^x` would be any
> better), and we chose a simpler rule: named constants are only
> recognized when referenced via some namespace, such as `mod.var` or
> `Color.RED`.
>
> We believe it’s acceptable that things looking like `mod.var` are
> never considered capture variables -- the common use cases for `match`
> are such that one would almost never want to capture into a different
> namespace. (Just like you very rarely see `for self.i in …` and never
> `except E as scope.var` -- the latter is illegal syntax and sets a
> precedent.)
>
> One author would dearly have seen Scala’s uppercase rule adopted, but
> in the end was convinced by the others that this was a bad idea, both
> because there’s no precedent in Python’s syntax, and because many
> human languages simply don’t make the distinction between lowercase
> and uppercase in their writing systems.
>
> So what should you do if you have a local variable (say, a function
> argument) that you want to use as a value in a pattern? One solution
> is to capture the value in another variable and use a guard to compare
> that variable to the argument:
> ```
>     def foo(x, spam):
>         match x:
>             case Point(p, q, context=c) if c == spam:
>                 # Match
> ```
> If this really is a deal-breaker after all other issues have been
> settled, we could go back to considering some special markup for
> load-and-compare of simple names (even though we expect this case to
> be very rare). But there’s no pressing need to decide to do this now
> -- we can always add new markup for this purpose in a future version,
> as long as we continue to support dotted names without markup,
> since that *is* a commonly needed case.
>
> There’s one other issue where in the end we could be convinced to
> compromise: whether to add an `else` clause in addition to `case
> _`. In fact, we probably would already have added it, except for one
> detail: it’s unclear whether the `else` should be aligned with `case`
> or `match`. If we are to add this we would have to ask the Steering
> Council to decide for us, as the authors deadlocked on this question.
>
> Regarding the syntax for wildcards and OR patterns, the PEP explains
> why `_` and `|` are the best choices here: no other language surveyed
> uses anything but `_` for wildcards, and the vast majority uses `|`
> for OR patterns.  A similar argument applies to class patterns.
>
> If you've made it so far, here are the links to check out, with an
> open mind.  As a reminder, the introductory sections (Abstract,
> Overview, and Rationale and Goals) have been entirely rewritten and
> also serve as introduction and tutorial.
>
> - PEP 622: https://www.python.org/dev/peps/pep-0622/
> - Playground:
> https://mybinder.org/v2/gh/gvanrossum/patma/master?urlpath=lab/tree/playground-622.ipynb
>
> --
> --Guido van Rossum (python.org/~guido)
> *Pronouns: he/him **(why is my pronoun here?)*
> <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
> _______________________________________________
> 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/LOXEATGFKLYODO5Y4JLSLAFXKIAMJVK5/
> 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@python.org/message/5LJWWKFZ2CGYA3HU3MVSPYDVIN4E4RV2/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to