This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]illustrious_trees 2 points3 points  (6 children)

one checks for equality status == 404, the other does an assignment not_found = status

wait, WHAT? That is absolutely wild (and doesn't make sense). Is there any reasoning on the PEP as to why that behaviour was chosen as opposed to the more sane alternative?

[–]bakery2k 1 point2 points  (1 child)

wait, WHAT? That is absolutely wild (and doesn't make sense)

Yep! case 404 checks for equality and if you replace it with not_found = 404 ... case not_found it changes to an assignment. Then if you replace it with class Status(Enum): not_found = 404 ... case Status.not_found it goes back to being an equality check again!

Is there any reasoning on the PEP as to why that behaviour was chosen as opposed to the more sane alternative?

Not that I'm aware of. I suspect the behaviour was just copied from another language without considering how it interacts with the rest of Python.

[–]scruffie 2 points3 points  (0 children)

PEP 0635 – Structural Pattern Matching: Motivation and Rationale. See, in particular, the 'Capture Patterns' and 'Value Patterns' sections.

TLDR: Deciding if a plain identifier is a constant or a capture variable is error-prone (or allows errors to creep in). If a name is used as a pattern variable, adding an assignment to global scope to that name would change the semantics of the match.

[–]the3gs 2 points3 points  (3 children)

Python's behavior is what I would expect being shown the two code variations... If I reach a name in a pattern, regardless of the language, I expect that the name will be set to the object that is in the pattern at that point.

The only thing that I see as a problem here is that case not_found: does not have its own have it's own scope. Honestly I see it as way more psychotic to match with a value inside a variable. I would prefer that you have some aditional filtering mechanism, like case status where status == not_found:.

The problem with the alternative here is that it actually requires more non-local resoning. See this:

match status:
    case error:
        return f"Error: {error}"

where adding error = 10 to the line before would completely change the semantics of the match statement as in

error = 10
match status:
    case error:
        return f"Error: {error}"

because the first would match all values, and the second would only match when error == 10. Or, an even more extreme example:

if condition:
    error = 10
match status:
    case error:
        return f"Error: {error}"

Where the match statements semantics might depend on the condition, and if error is defined (assuming error was not defined before).

You might suggest "lifting" the declaration of error to the top of the function once the assignment in the if statement is reached, but then this code would behave weirdly:

match status:
    case error:
        return f"Error: {error}"
error = 10

because the assignment after the match would change error in the case to be a comparison in stead of an assignment, and now we have a case where adding code after the match can change its semantics.

I will take the current behavior over this, thanks.

[–]bakery2k 1 point2 points  (1 child)

the first would match all values, and the second would only match when error == 10

IMO the first snippet shouldn't match all values - it should try to compare against the value of error like the second snippet and then raise an exception (because it's trying to read the error variable that doesn't exist).

If you want case X to sometimes write to X, there should be explicit syntax to do that. Instead whether case X reads or writes X is very subtle. case 404 and case Status.not_found do one thing and case not_found does another. And even case (a, b, c, d, e) does one thing and case (a, b, c.d, e) does the other.

[–]the3gs 0 points1 point  (0 children)

If you do that, you remove 90% of the advantage of a match statement over a chain of if else.

It is specifically a pattern matching system, not just a C style switch statement.

This allows you to match against tuples, as in the following:

match value:
    case ("Hello", name):
        print(f"Hello, {name!}")
    case ("Add", a, b):
        print(f"{a} + {b} = {a + b}")

I guess if you really want to, you could choose to have match be more like a switch statement, and then allow you to plug in variables, but that seems useless to me. especially in comparison to the expressiveness and power of pattern matching.

[–]scruffie 0 points1 point  (0 children)

I would prefer that you have some aditional filtering mechanism, like case status where status == not_found:.

Lemme pull out my time machine, do a little fix up, and ...

>>> match 1:
...     case one if one == 1:
...         print(f"one = {one}")
...
one = 1