93 comments

[ 4.1 ms ] story [ 163 ms ] thread
This tutorial us great I can't see why a language feature that counterintuitive us introduced. I played Dungeon games with more streightforward riddles. Perhaps this feature could be utilized for gaming quizzes.
I'm not sure what this comment is actually trying to say. the feature seems very intuitive and easy to understand.
It might seem intuitive to some people but my comment (which was the first after the post) said that its very prone to errors. Not saying much more than the other comments that were added in this discussion.
I like using pattern matching as an if statement against the type system like the last example in this tutorial, where the author matches against the different variants of an enum. I know this is not the case for the Python interpreter, but it has always amazed me how much types can narrow the code paths in generated assembly instructions.
> You can combine several literals in a single pattern using | ("or"): `case 401|403|404:`

So how do I do a bitwise-or operation within a pattern?

I think given issues like this and the commonly-cited NOT_FOUND example [0], it might have been better to only allow pattern matches on compound data and disallow its use with simple values. That’s where pattern matching is most useful, as in the article‘s second example (`match point`), plus it would make it more obvious that the behaviour of `match` is different from C’s `switch`.

[0] The following code unintuitively assigns the value of status_code to the variable NOT_FOUND:

    NOT_FOUND = 404
    match status_code:
        case NOT_FOUND:
            print("HTTP Not Found")
Edit: OK, it looks like you can’t do arithmetic/bitwise operations in a pattern at all, so there’s no direct clash between bitwise or and this use of `|`.

Therefore if you need to match against a calculated value, you can’t do the calculation inline. Instead you need to calculate the value outside the match and give it a name - but you can’t match against a named local variable either!

With enum.Flag and guard statements?
As an Erlang fan, I can definitely see myself falling into the NOT_FOUND trap.

I frequently miss immutable data when working in Python; in general I’m conflicted about languages that retrofit functional programming features, because immutability is such a huge benefit.

As a Prolog fan, I'm conflicted about languages that retrofit logic programming features (such as pattern matching) because term unification is such a huge benefit ;)
As a guy who built an interval arithmetic constraint programming system, based on minikanren, in python for his PhD, I agree, unification is pretty sweet!
Oi, you can't leave us hanging like that! I'd really love to read it if you have a link, even more so after the extra paragraph in your HN bio.
Fun fact: erlang was originally implemented in prolog, its since been rewritten in C. Many of the features in the language were inspired by prolog.
i'm not sure "immutable data" is precise as a complaint here - this is akin to the difference between `const` and `let` in Javascript, which has nothing to do with immutability of the data and everything to do with the immutability of a name binding.

I do wish we had better first-class immutable data in Python, but that wouldn't help with the

  NOT_FOUND = 404
  match val:
      case NOT_FOUND:
          ...
scenario that everyone is (rightly) fixated on, because integers themselves are already immutable.
True, I tend to conflate immutable bindings with immutable data. With Erlang, all bindings are immutable, which makes pattern matching more powerful (although it can lead to unexpected crashes on occasion).
> scenario that everyone is (rightly) fixated on

Your "rightly" obviously varies, but this scenario is the poster child for enums, and you can match on enum members. The featured article has an example.

You can make any code unintuitive by naming the variables wrong. Consider:

    LAST_MATCHED_STATUS_CODE = 404
    match status_code:
        case LAST_MATCHED_STATUS_CODE:
            print("HTTP Not Found")
That's still unintuitive or at least strange because uppercase generally indicates constants.
Renaming the variable doesn’t fix the code. It still prints “HTTP Not Found” for every value of `status_code`.
right.. D'oh

I gather that the proposed solution is to use something like enums? Or not do this at all?

Yes, or not import the constants from their module aka

    import foo
not

    from foo import BAR, BAZ, QUX
My gut tells me the logical `or` here should use the word "or" like in the rest of the language.

Neat syntax overall but it's not clear what it makes more succinct than the equivalent `elif` statements.

Agreed regarding the "or" - rejecting consistency here just because other languages do it differently is weird.

> it's not clear what it makes more succinct than the equivalent `elif` statements

But pattern matching is shorter than if-else in many cases and can actually differentiate between structures which makes it very flexible. It's like switch-case should have been from the start imho.

As an example, in Rust I often do comparisons between certain values by simply matching a tuple of them:

    match (foo, bar) {
        (x, x) => true, // foo == bar
        (Foo::VariantA(42), _) => unreachable!(), // has to be a bug
        (Foo::VariantA(a),  _) => a < 0,
        _ => false,
    }
This is of course a rather simple example, but I hope it still conveys what I like so much about this kind of feature. It doesn't do more than an if-else chain, but under the right circumstances it really shines. Just like list comprehensions don't add any new functionality, but the filtering and mapping features are often simply more expressive for what you want to do.

Although I'm not sure what to think of Python's preference to put the condition and code in different lines; maybe that's optional, which could make it more succinct when the situation permits it.

I was worried about that example, but the problem doesn't seem to be too bad, because the spec says:

« A match statement may have at most one irrefutable case block, and it must be last. »

So if I understand correctly the following will be an import-time error.

    NOT_FOUND = 404
    I_AM_A_TEAPOT = 418
    match status_code:
        case NOT_FOUND:
            print("HTTP Not Found")
        case I_AM_A_TEAPOT:
            print("HTTP I'm a teapot")
Agreed. They should have required an extra token like ? to disambiguate bindings from evaluations. Not only would it avert this pitfall, it would also obviate the need to have a special identifier to discard a bound value; instead of writing _ to discard a binding, one would just write ? without a variable name.

I fear a poor design like this is going to not only discourage people from using the feature at all, it is also going to poison the well and discourage people from using pattern matching in languages with a much more robust implementation. Which is a shame, because I actually like pattern matching.

(Tinfoil-hat take: Maybe that’s the whole point, after all Guido doesn’t like functional programming.)

Since the names have to be dotted anyway, why not .var_name? I feel like this implementation is seriously half-baked.
I could be wrong, but this PEP feels to me like it only got accepted because Guido is stepping (or stepped) back from his BDFL role. It has noticeably more rough edges and less consistency than most feature adds I have seen in the past. This is pure personal speculation though.
Guido sponsored the PEP so I doubt him being BDFL would have stopped it.
> [0] The following code unintuitively assigns the value of status_code to the variable NOT_FOUND:

This seems sensible and intuitive, `NOT_FOUND` is not different than `not_found` or `bob`. It follows the normal rules of Python bindings.

I'm way more annoyed by the special case for dotted names.

The problem is that `NOT_FOUND` behaves differently from the literal `404`, which breaks an important principle: “if you see an undocumented constant, you can always name it without changing the code’s meaning” [0].

[0] https://twitter.com/brandon_rhodes/status/136022610839909990...

That is true, but for Python never having had constants. So as soon as you name an undocumented constant, you run the risk that people will update it. So that's less a principle and more a guideline.

And hey, if it finally gets explicit variable declarations in Python I'm 100% all for it. implicit locals, `global`, and `nonlocal` are awful, viva `let` and `const` or whatever.

As to the content of the tweet

> The Substitution Principle, learned in algebra?

Python is not algebra, and already breaks algebraic reasoning to and through. Just consider that `a = a + b` and `a += b` are completely different operations with possibly (and actually, for some types) completely different effects.

> `a = a + b` and `a += b` are completely different operations

Getting off-topic, but IMO that’s a major design flaw in the language. Some others agree, e.g. https://twitter.com/joepolitz/status/1304992696293978112

Oh I completely agree it’s a major design flaw of the langage and I was dismayed the core devs used it again with the in-place dict update of 3.9.

My point was that if you were teaching people that pull thon follows strict algebraic principles you were already setting them up for a rude series of surprises.

I don't think I have ever used a language where this is not the case. You match against a pattern, not against the result of an expression.

It is not that confusing once you have grooked it.

In some languages, however, data constructors live in a separate namespace from variables and in some they do not.

In ML and Rust, mistyping a a data constructor's name produces a variable, and thus matches everything, whereas OCaml and Haskell require that constructors start with an uppercase name, and disallows variables to do so, so this cannot easily occur.

But, since in Rust data constructors are typically qualified with a namespace except for some frequently used ones, this is not a problem in practice.

It’ll also often produce one or more warnings in Rust, which can help.
Indeed. — that Rust warns against unused variables and has language facilities to suppress such warnings if ever they be so intended has no doubt caught many typos that other languages have not.
> That’s where pattern matching is most useful, as in the article‘s second example (`match point`), plus it would make it more obvious that the behaviour of `match` is different from C’s `switch`.

Meanwhile, the top post on /r/python for the last few days has been "Ladies and gentlemen - switch cases are coming!" This feature and its footgun syntax are going to be a disaster for the casual Python audience.

(comment deleted)
It's worth noting that a properly-configured linter will give you an unused variable warning here, since you've bound to NOT_FOUND but not used it for anything.
That assumes it isn’t used again later.
> Therefore if you need to match against a calculated value, you can’t do the calculation inline. Instead you need to calculate the value outside the match and give it a name - but you can’t match against a named local variable either!

Can you do either of those in other popular languages with pattern matching? Presumably the reason for wanting pattern matching wasn't to invent something completely new, but to implement something similar to pattern matching in other languages. (I know that C-style switches allow you to write constant arithmetic expressions in case labels.)

> Can you do either of those in other popular languages with pattern matching?

Ruby supports matching against a local variable by pinning the variable (using a ^ prefix).

I am aware that this is just an example for the apparent case of bad design, but in this particular example I'm always advising developers against using plain integer for HTTP status code - http.HTTPStatus enum is a much better solution.
Why not just post PEP 636 -- Structural Pattern Matching: Tutorial

https://www.python.org/dev/peps/pep-0636/

Personally found this GitHub repo easier to follow.

It also includes a "launch binder" button which fires up a Jupyter Notebook running a fork of Python with the new syntax ready for people to try out interactively.

Agreed; this explained things better for me than any of the other articles I've seen on the subject. I got to the "guard clauses" before horking this time.
Though that tutorial is comprehensive, as someone that's already relatively familiar with pattern matching, I've personally been looking for something a bit more terse. Mostly that points out the nuances of it in Python and this isn't too bad.
I am concerned by the number of new features that are being added to the Python language. Python is a beginner friendly language, but every new feature makes it a bit harder to learn.

To be honest, I kind of enjoyed that the last decade when Python was frozen at 2.7 for users most of the time.

I think that it is possible to be beginner friendly, while also having higher level concepts that you can learn. I actually think this is what makes it an amazing progressive language to learn. You can continue to learn new things about the language almost perpetually.

I understand arguments that simplicity means easier to learn, but also think that in most subjects in life we start with the basics and then break those rules to add nuance and continued expansion to the topics. See math, language, science.

>I think that it is possible to be beginner friendly, while also having higher level concepts that you can learn.

I mean, you can say that about C++. You use it like C (it's a superset of C, after all), and gradually learn the features (STL, template meta-programming, polymorphism, etc.) as you go along. But I doubt that many would say c++ is beginner friendly.

That approach can work if you assume everyone is flying solo.

As soon as you get into a team setting, though, all these advanced features become a way for more advanced users to unwittingly create a hostile environment for less skilled programmers.

Which maybe isn't a big deal in the software engineering department, where you can perhaps assume that everyone is a crack programmer. It can be a huge deal, though, for a lot of non-software-engineer departments that still spend a lot of time working with code. Those teams tend to be heavily dependent on languages like Python.

That logic makes sense. I am all for writing as simply as possible, and making code readable. But I understand that some may not be interested in that. I do know that some code bases tend to be more difficult to step into than others. I don't know if we can blame a language for this, or if we should be blaming the programmers. The language is a tool we can use to execute a task, but also to communicate how we are executing that task. If a developer has not written code in a way to make that obvious (or supplemented with comments), is it the fault of the language or the fault of the developer?

Of course, by extension, some languages have more complicated syntax (perhaps "less intuitive" based on languages you may be used to).

All said, I agree with you that some developers will indeed use higher level concepts which may not be understood immediately by lower level developers. I think this would occur even without those concepts embedded in the language itself, as the higher level developer would need create a solution to solve the problem anyway.

The NIMBYism is strong in this thread. There is nothing about this feature that will make Python any harder to learn for newcomers. You can choose not to use it.
> You can choose not to use it.

I hear this a lot about features in Python. Type hints, dataclasses, walruses, and now pattern matching. Whenever someone complains about language bloat, the reply is “it’s optional”.

But that’s only true as long as you’re writing code by yourself. Most developers spend much more time reading code than writing it, and with no control over which features others use, it’s necessary to be comfortable with the entire language.

On the flip side: whole projects are stuck on older versions so even though something like the walrus is interesting and could be useful no one else on the team has any interest in doing the work of switching to a newer version so none of the new features matter.
> you can choose not to use it

What's more, you can even choose not to understand it. An easy way to do that is to choose not to work with other people's Python code.

Don't apply for Python jobs, and in a non-Python job, dodge tasks that involve touching Python code that contains constructs you don't wish to understand.

Gotta make up your mind here - are we complaining about newcomers to the language or professionals? If you are a professional programmer there is nothing about this change that should phase you. In fact, if you are a professional Python programmer this should be an exciting addition.

My argument still stands. I am so sick of people complaining about progress, especially armchair quarterbacks on HN.

I'm only saying that the "don't use parts you don't like" argument has limited applicability.

We have to acknowledge that some newcomers are eager to learn everything, and that a bigger everything is harder than a smaller everything.

That's just a fact; it doesn't meant that a smaller everything is better.

Now that Python has pattern matching, newcomers should learn it.

In my opinion, newcomers should learn pattern matching before they learn lower-level, more verbose ways of writing code for classifying objects for type, shape and content.

The more verbose code should be the appendix, like "in older Python before pattern maching, we would have had to write it like this ...".

Or, to put it alternatively, newcomers no longer should learn how to use basic primitives like if comparisons and whatnot, to write code which combines multiple tests for object type, shape and content in multiple cases. They should be steered toward pattern matching, with a caution about how that is new and doesn't work with Python versions lower than x.y.z.

Pattern matching is particularly good for beginner programmers, because beginners programmers are not good at planning the design of a program ahead of time: they don't have an intuition for the right data structures and such. Pattern matching can patch over some representational difficulties/tensions in the program so that the code looks good and is easy to reason about and debug.

If all you have is a switch/case type statement, you might be told that, at some point, the pain you're feeling is from abusing that and should switch to something else, like proper OOP. In some of those same situations, you could instead switch to pattern matching. Pattern matching is less abstract and more literal-minded, since it uses "pictures" of data, which can overcome disparities in its structuring in a way that looks obvious in the code. Pattern matching has processing "under the hood", but what that does is not only largely intuitive, but also localized to the spot in the program where it is happening. I believe all of that favors the skill level of newbies.

The pitfalls in this miserable implementation are another story, but I think that will only help Python. Pitfalls bring Python people together to form communities and help each other. People band together and cooperate when they have common enemies, like sabre-toothed tigers, inclement weather or shitty programming languages and systems. The six-week-old Python newbie is eager to explain the quirks of pattern matching to the day-old newbies, and so it goes.

Other than the Python-specific quirks, newbies will likely struggle with bugs involving the order of pattern matching cases, when they place a more general case before a more specific one, such that it unexpectedly takes some (or even all) matches from that one. Adding new cases to a pattern matching block ahead of existing cases is tricky, if they match anything in common.

With pattern matching, you can write a small amount of code that handles a lot of ad hoc complexity---and if that doesn't have test coverage, that can spell trouble.

But you can't choose that other are not using them. If you read other peoples code, and beginner will do that a lot at some point, then you will likely encounter unknown syntax. Good if you read if for that purpose, bad if you just wanna enhance/fix the code.
is unknown syntax that much more of an issue than unknown function/library calls? I very much doubt it. If it were, the industry would have settled on languages with way less syntax a while ago, e.g. Clojure.

At least with unknown syntax there's usually some kind of keyword you can look up with Google. It's generally the non-syntactic sorts of unknowns that are much harder to figure out for a given codebase.

It's a much easier to look up library functions than language keywords in Python docs, and it's one of the first things Python programmers learn.
False. There are multiple documented footguns and inconsistencies that will baffle newcomers and pros alike.

The quality of this proposal is much lower than other new features of the last decade.

I think that pattern matching is actually more intuitive than say, decorators for example (beginners I came across usually have a hard time with those).
Python shall be seen in the same light as BASIC and COBOL in the future: the language that taught a generation very poor programming practices it later had to unlearn.
I can never wrap my head around Python being beginner friendly because as a software engineer who knows languages like Racket, F#, and Elixir, I find Python extremely confusing beyond anything basic. It’s async model (or models), where the GIL applies and doesn’t, scoping rules, class model (or again, models), decorators, etc. are all confusing and extremely complex. Given these and other quirks, it makes its dynamically typed nature a walking time bomb.
You listed

> async model

> GIL

> scoping rules

> decorators

but these are not relevant for a beginner in programming, which is I think what advocates refer to.

Python makes it easy to read files, do something with the content and write it back, to print any variable. It runs even if you have syntactic errors in an unused function. A beginner's Python code can be dirty, but I think one reason why it's taught so often is because it's less frustrating to fix runtime bugs than cryptic compilation errors you see in safer languages, although as you said, it's detrimental to the code quality in the long run.

Okay, but if Python already has all of these things that are considered not relevant to beginners, why assume that these new language features are things beginners will have to deal with, instead of being like the language features they don't?
The feature in question matters. I don't see how you would fit async / the GIL in a beginner course. But if pattern matching is seen as idiomatic for basic code, then it should and will be introduced to beginners.

For instance the switch / case construct is on https://learnxinyminutes.com/docs/java/ (interestingly not on https://learnxinyminutes.com/docs/c++/ !), implying that it's quite basic there.

I would argue that those things don't even need to be there or so complex, which is why it's weird Python is considered a good beginner's language. It is Python that makes these things complex, not the things themselves. The GIL shouldn't be there in the sense of async and concurrency code being understandable and actually concurrent. The scoping rules should absolutely be explainable to beginners. Decorators are just weird tacked on things. Maybe async is a non-beginners topic, but one can introduce it pretty easily in languages like F# or Elixir or even LabVIEW, so I'm not sure.

Plenty of languages make it very easy to read data from a file, process it, and then write it back out. F#, Elixir, Racket, LabVIEW, etc. all come to mind.

It depends on how approachable a feature is. Is it self-explaining or cryptic. Easy or hard to explain and understand. Obvious to google even when you not know the name for it.

In those regards most new python 3-features are more on the good side. I know how often I googled for old python features like string substitution (the %-thing) or list comprehensions (the loop inside []) and I just could not figure out and remember the names, because % and [] are simply not self-explaining.

> Python is a beginner friendly language

Not really, no. It hasn't been beginner-friendly for a decade or so now.

I think pattern matching is super powerful, reduces boilerplate, and is not that hard to understand if implemented well.

I also think those who complain about the decline of Python's beginner-friendliness need to take a step back and understand that this is one of the most popular languages in use today. Reducing code maintenance issues (by eliminating boilerplate) will save many programmers time, and when you multiply average savings per person by the total number of programmers using Python day-to-day, I think adding certain features is a net positive. The time taken to learn this feature is small, and it's a one-time investment for each programmer. I don't think this is even a difficult feature to learn at a coarse level, since parameter unpacking/binding is already part of Python (think `for k, (x, y) in enumerate(list(zip(xs, ys)))...`.

All that said, I'm a bit concerned about how this is implemented. Many have raised the issues of unintuitive binding of non-dotted names, lack of local scope, and overloading of the `|` operator. I can't understand why these problems were not addressed during the PEP process. Does anyone have any insight?

I only vaguely looked into all the conversation that went into this feature. But my sense is that, in a nutshell, folks figured out, after a lot of really impressive work on ironing out all the details, that there just isn't any to integrate this feature into the existing language without introducing footguns. The decision then came down to, "Do we drop the feature, or push it through anyway, warts and all?" And some combination of strong desire for pattern matching at any cost and anxiety over sunk costs means that the second option won.

IOW, design by committee doing what design by committee does best.

lack of local scope, specifically, seems tied to Python's general lack of scoping beyond function contexts. Which is definitely a footgun for people coming from languages with full block scoping (which I strongly prefer), but at the same time I can see an argument for it making a lot of sense to most beginners to programming, who "look upward", see a name, and wonder why they don't have access to that name.

I agree about the unintuitive binding of names, though presumably the rationale is simply that any other approach would have introduced a significant amount of boilerplate. The only syntax I can think of would require a "bare as", e.g.:

  FOUR_HUNDRED = 400
  NOT_FOUND = 404
  match code_and_val:
      case (FOUR_HUNDRED, as x):
          print(x)  # FOUR_HUNDRED is not bound at all, but simply matched
      case (NOT_FOUND as x, as y):
          print(x, y)  # prints 404, code_and_val[1]
which looks quite weird to a Python programmer, though in this case I suppose you could argue that explicit is better than implicit.

not sure why the `|` overloading is a big deal here? there's no other significantly useful meaning for it in this context. Can you be more specific?

Realistically, any practical argument for Python's lack of block scope is probably just sour grapes. The real reason, I'm guessing, is that it started out as a hobby project 30+ years ago, and that block scope is not something GvR personally felt was worth the effort to implement at the time.

Followed by three decades of not wanting to do that particular breaking change, because doing so would be far more painful than anything that happened in Python 3.0.

> ...because doing so would be far more painful than anything that happened in Python 3.0

Is this really true? I don't think lack of block scope is ever used for anything but causing bugs.

Could do it the same way JS did it, with const and let keywords. Breaks nothing.
Pattern matching could be amazing for Python. I love pattern matching in erlang/elixir.
And is it going to be anything similar in Python? From the looks of it, Python's pattern matching is nothing like OCaml's or Erlang's, but perhaps I am wrong.
Well, Erlang is built around pattern matching as a core concept that applies to every statement, so it’s safe to say it’ll be much more limited in utility in Python.

Still, it looks like a decent addition.

Most of it wasn't written by GvR, same as the PEG parser, but nice to see that always the same roughly 10 people get publicity and credit in the exploitative Python "community".
In true Python style, the variable captures will no doubt use assignment rather than creating a new scope, and will lead to all sorts of wonderful and hidden bugs.

Regardless? Is there some protocol to allow user defined classes to be matched? One of the situations with algebraic datatypes is that it's very clearly defined what can and cannot be matched so that nothing is special and built in, but it also requires that concepts that should honestly be private be exposed. — it would perhaps be better to have some kind of protocol whereby some sort of “match object”is returnen for this functionality.

The assignments use the usual goofy python scoping rules and implementations are allowed to make this a footgun. Consider this (contrived for the sake of simplicity) example:

  for x in input:
    backup = x
    point = f(x)
    match point:
      case Point(x = 6, y = 9):
        print('found a nice point')
      case _:
        print('not nice point')
        assert x == backup # this may fail
The first case is allowed to bind x if point.x == 6 even if point.y != 9 and the case fails. Reassigning x in the surrounding scope is annoying but expected in python. Reassigning even when the case fails is not at all expected imo.

https://www.python.org/dev/peps/pep-0634/#match-semantics

> During failed pattern matches, some subpatterns may succeed. For example, while matching the pattern (0, x, 1) with the value [0, 1, 2], the subpattern x may succeed if the list elements are matched from left to right. The implementation may choose to either make persistent bindings for those partial matches or not. User code including a match statement should not rely on the bindings being made for a failed match, but also shouldn't assume that variables are unchanged by a failed match. This part of the behavior is left intentionally unspecified so different implementations can add optimizations, and to prevent introducing semantic restrictions that could limit the extensibility of this feature.

What a disaster. Breaking the rule that literals and variables are equivalent as r-values is terrible for the language. Python already has the walrus operator — it’s a real shame that they decided not to leverage it for disambiguating binding and matching in pattern matching.

    match status_code
        case NOT_FOUND:=_ :
            print(“clearly we’re binding a variable here”)
        case _:=NOT_FOUND:
            print(“and here we’re matching”)
Pretty much all languages that implement pattern matching treat case statements as l-values, not r-values. `match x: case y: ...` basically means "execute `y = x`, enter the body if it succeeds, try the next case if it fails". Python does allow more things as a case than it does in an assignment, but with the exception of `case enum.NOT_FOUND` (which IMO is a mistake and should not have been allowed), it could use the exact same syntax for `y = x` and `case y` if it wanted to, and it would be perfectly logical.

In the programming language I made a while back, my match statement was similar to Python's, and every case statement was also a valid l-value, so you could write `Point(x, y) | (x, y) = something` or `123 = x`, which would fail unless `x == 123`.

Using _ as a default doesn't seem great, when it's used by gettext in so many apps.

I wonder if they could have used * for the wildcard.

I hope they can avoid having two block indents, like in those examples. Match could be followed by a case in the same line, and extra cases in new lines.

  match val case True:
      print("Ok")
  case False:
      print("Not ok")
  
  print("Done")
they discussed various options for less indentation and rejected them. this syntax is definitely out of the question.
I was rather disappointed when I saw the final(?) state (or at least now accepted state) of the Pattern Matching PEPs the other day. I could write a rant now but given the fact that pattern matching was a hotly debated[0] feature proposal, I assume my complaints must have all been considered at some point and there were good reasons to still go with the present PEPs. Unfortunately, there's little to no sign of this debate in PEPs 634, 635 and 636, so I hope some Core Developer will see my comment and have answers? Maybe then I'll make piece with this new feature.

1. Why not make patterns objects like pretty much everything else in Python (in particular: types, context managers, etc.)? I'm disappointed that it will be impossible to re-use patterns.

2. Why is the syntax for placeholders the same as for variables in usual Python expressions? Why not go with with `?placeholder` or `$placeholder`? I'm aware of the "symmetry" between construction and deconstruction that PEP-635 mentions[4] but in this case I find it rather disadvantageous: It will now be extremely cumbersome to insert constants into a pattern, as it will have to be done either through guards[1] or through attribute access[2]. More importantly, though, Python's syntax has now become extremely confusing over night – some identifiers in the pattern refer to placeholders, some others to existing variables. Sure, this is no different in function declarations that include type hints and default values. But the syntax here is a lot more complex and there is no clear line of separation as with function calls where the ":" and "=" tokens make it clear which identifiers are placeholders and which are existing objects. I am aware that PEP 640[3] was rejected but was it because of the question mark syntax or because of the fact that it tried to change lots of language features at once with no real benefit? (I myself would have rejected it for that reason, too.)

3. What was the idea behind overloading the class/constructor syntax as in `match [str(my_string_placeholder), foo]`? I can think of a lot alternatives and I have yet to find one that I find as confusing as this one.

4. Coming back to the idea from 1) of making patterns ordinary objects, such an object would basically be… a type. Or, put differently, pattern matching amounts to structural type checking plus the matching part. But Python already has structural typing through type hints, generic types and Protocols. The only drawback – apart from a couple pitfalls with Protocols – is that it is still impossible to use isinstance() to do check against generic types. My personal impression, though, is that a lot of people want that feature and that it will ultimately arrive. Or at least this is my hope. Then, however, we will have two ways to do structural type checks, which is clearly at odds with the Zen of Python. Did anyone consider this interplay of patterns vs. generic types? What will the future of generic types be? My complaint here is basically that the pattern matching PEP doesn't go far enough and that pattern matching on its own is a nice but rather niche feature. I think I would have liked a general way (or syntax) to describe the structure of objects in Python – but instead of improving the one existing way (type hints with generics and Protocols) we now have two suboptimal ways.

[0]: https://lwn.net/Articles/845480/

[1]: https://www.python.org/dev/peps/pep-0635/#guards

[2]: https://www.python.org/dev/peps/pep-0635/#id24

[3]: https:...

I’ve been tutoring grad students in Python for the past couple years, and tbh it’s not a great beginner language, it’s not a great advanced language, and it’s not efficient or elegant. I do not recommend Python to beginners anymore.