9 ms·
Python Pattern Matching
- antman 6y agoThis 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.
- optimuspaul 6y agoI'm not sure what this comment is actually trying to say. the feature seems very intuitive and easy to understand.
- antman 6y agoIt 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.
- css 6y agoI 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.
- pansa2 6y ago> 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!
- ok123456 6y agoWith enum.Flag and guard statements?
- macintux 6y agoAs 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.
- tannhaeuser 6y agoAs 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 ;)
- tobmlt 6y agoAs a guy who built an interval arithmetic constraint programming system, based on minikanren, in python for his PhD, I agree, unification is pretty sweet!
- JNRowe 6y agoOi, 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.
- ihumanable 6y agoFun fact: erlang was originally implemented in prolog, its since been rewritten in C. Many of the features in the language were inspired by prolog.
- ledauphin 6y agoi'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.
- fireattack 6y agoWhy not just post PEP 636 -- Structural Pattern Matching: Tutorial https://www.python.org/dev/peps/pep-0636/ https://www.python.org/dev/peps/pep-0636/
- simonw 6y agoPersonally 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.
- h2odragon 6y agoAgreed; 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.
- ZeroCool2u 6y agoThough 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.
- jakuboboza 6y agoPattern matching could be amazing for Python. I love pattern matching in erlang/elixir.
- johnisgood 6y agoAnd 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.
- macintux 6y agoWell, 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.
- ma2rten 6y agoI 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.
- christofosho 6y agoI 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.
- gruez 6y ago>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.
- mumblemumble 6y agoThat 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.
- christofosho 6y ago
- r-zip 6y agoI 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?
- mumblemumble 6y agoI 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.
- ledauphin 6y agolack 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?
- cxcpp 6y agoMost 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".
- Blikkentrekker 6y agoIn 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.
- iamevn 6y agoThe 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 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.
- bobbylarrybobby 6y agoWhat 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”)
- amai 6y agoThere is a discussion on this issue here: https://github.com/gvanrossum/patma/issues/140 https://github.com/gvanrossum/patma/issues/140
- breuleux 6y agoPretty 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`.
- JNRowe 6y agoLast week's discussion on this has over 500 comments, many of which are quite insightful. https://news.ycombinator.com/item?id=26080760 https://news.ycombinator.com/item?id=26080760
- stuaxo 6y agoUsing _ 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.
- sizeofchar 6y agoI 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")
- ledauphin 6y agothey discussed various options for less indentation and rejected them. this syntax is definitely out of the question.
- codethief 6y agoI 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/ https://lwn.net/Articles/845480/ [1]: https://www.python.org/dev/peps/pep-0635/#guards https://www.python.org/dev/peps/pep-0635/#guards [2]: https://www.python.org/dev/peps/pep-0635/#id24 https://www.python.org/dev/peps/pep-0635/#id24 [3]: https://www.python.org/dev/peps/pep-0640/ https://www.python.org/dev/peps/pep-0640/ [4]: https://www.python.org/dev/peps/pep-0635/#id28 https://www.python.org/dev/peps/pep-0635/#id28
- stelcodes 6y agoI’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.
- hanselot 6y agoA part of me really believes Python is being embraced. https://news.ycombinator.com/item?id=25071847 https://news.ycombinator.com/item?id=25071847