7 ms·
Honestly I'm not sure what all the fuss is about. I just skimmed over PEP 634 and PEP 636 (the accompanying tutorial) and I'm actually kind of excited to use t
by davesque 6y ago
Honestly I'm not sure what all the fuss is about. I just skimmed over PEP 634 and PEP 636 (the accompanying tutorial) and I'm actually kind of excited to use this. I've missed this feature after using langs like Rust, Haskell, and Scala. People have pointed out some surprising edge cases such as this:
NOT_FOUND = 404
match get_status():
case NOT_FOUND:
# this actually assigns to the global NOT_FOUND
# and is effectively the default case
case _:
# this never gets triggered
Yeah, I agree that's a bit ugly. But you can apparently do this:
class Status(Enum):
NOT_FOUND = 404
match get_status():
case Status.NOT_FOUND:
# works as expected
...
And really how is any of that more ugly than this?
def func_with_default(foo={}):
if "bar" not in foo:
# "bar" is set in the module-level dict that
# was initialized when the func def was parsed,
# not in a dict that is created per call
foo["bar"] = ...
...
And we've been living with that one for years. These are just the things that you have to learn when you start using Python, just like any other language.
I, for one, am excited to start using this syntax.
- codesnik 6y agomodule-level dict?... why...
- davesque 6y agoUh...that's my point. Everyone new to Python has to eventually learn not to do that.
- PartiallyTyped 6y agoThe folk over at openAI do something similar in order to create a registry, so that you can initialize objects using a name.
- carapace 6y agoIt's a simple rule: Default arguments are evaluated at function definition time (when the interpreter reaches the 'def' statement) not at the time the function is called. It's not a "module-level dict", you can create functions inside other functions: def foo(): def bar(a={}): ... return bar Each instance of bar gets its own dict when you run foo() and the "def bar(...): ..." statement gets executed. The dict is packaged up inside the function object (sort of like a closure except that the dict and it's name aren't from the enclosing scope of the function, and of course you can "shadow" it by passing in an argument for the parameter. https://en.wikipedia.org/wiki/Closure_(computer_programming) https://en.wikipedia.org/wiki/Closure_(computer_programming) )
- codesnik 6y agoI understand this simple rule, and I understand closures, of course, it's just using closure semantics for default arguments doesn't make any sense, aside from some optimization maybe, and pretty dangerous. Other approaches are possible, see ruby, for example, default arguments are evaluated basically as if they're already in the body of running function, every time, only when they're not passed, and with the same lexical scope as the function/method. And what, python retained this behavior even in 3.x? omg.
- carapace 6y agoAh, sorry for over-explaining.
- jgtrosh 6y agoThe behaviour my intuition would have expected is that if a variable is currently in scope, then it is considered as a literal match, otherwise it's a capture match. I suppose there's an obvious flaw to that behaviour, though.
- pfalcon 6y agoNo, that's not robust. The idea was simple - to use explicit marker like: case == FOO: case is True: That was part of PEP642, but it ended up containing more other questionable things. I hope that particular point will be separated and implemented.
- aidos 6y agoYou’ve braver than me, I had the same thoughts but didn’t dare post in here. Not entirely surprised by the negative responses and I can see there are gotchas here, which is always thorny. But everyone seems to be picking simple uninteresting use cases so they can knock then down. This feature would seem to be for cases where you need to do more complex matching. In terms of the variable assignment, surely the whole point of this feature is to give you nice assignment based on matching, rather than using it as an if or switch statements?
- dataflow 6y ago> Yeah, I agree that's a bit ugly. It's not about ugliness, it's about clarity and robustness. It's about not ingraining landmines into the language, whether beautiful or ugly. This is just asking to be blown up in people's faces over and over again.
- toxik 6y agoA previous mistake does not make a convincing argument for making a new mistake. Also, the mutable default arguments problem is quite difficult to solve in any other way. Python never copies things for you. Pattern matching is a desired feature, but the shortcomings are too costly for what it provides. You just outlined yourself the quickest foot-gun I've ever seen. My second problem is that this violates many of the invariants you rely on while programming Python. f(x) always gives you the value of applying f to x. Nope, not in case. | means bitwise or. Nope, not in case. a means the value of a. NOPE. Not in case.
- pfalcon 6y ago"f(x)" and "|" inside a regular expression mean something different. Similarly here, pattern matching has its own sub-language (a DSL).
- toxik 6y agoCoincidentally, regular expressions are not part of the Python syntax. They're just string literals.
- craftinator 6y agoAre you being intentionally ironic here, or do you not know that regex DSL is written only inside of strings in python, not within the Python's syntax?
- globuous 6y agoIsn't it the same in ML though (using the same syntax in different contexts) ? Compare `| [a, b] => a + b` and `let my_list = [a, b]`. Same in JS, `let {a, b} = {a: 1, b: 2}` and `let d = {a, b}`. It's weird at first but then you just get used to it. edit: updated list name since pipe ("|") and small L ("l") look kind similar in the code snippets.
- contravariant 6y agoA variable name has never referred to the value of it in expressions where it is being assigned. In 'def f(a,b,c)' or '(a,*_) = x' the term 'a' doesn't refer to the value of a either. A match statement in this sense is just an assignment that has a graceful failing mechanism (rather than e.g. '(a,*_) = x' which throws for empty lists). And I think the ship on '|' has sailed with the dictionaries.
- EwanToo 6y agoFor what it's worth, I tried a modified version of the first example you've used above in the sandbox: NOT_FOUND = 404 match get_status(): case NOT_FOUND: print("Here") # this actually assigns to the global NOT_FOUND # and is effectively the default case case _: # this never gets triggered print("There") I got a meaningful error: File "<ipython-input-13-5264d8327114>", line 3 case NOT_FOUND: ^ SyntaxError: name capture 'NOT_FOUND' makes remaining patterns unreachable
- hyperbovine 6y agoThat in itself is weird though. Why throw a syntax error there but not here?: def f(x): return 1 y = x + 1 return y
- deleted 6y ago[deleted]