Binding a new variable automatically in match-case should be explicit

Currently we need to do ``match x: case ==y if we need to compare, because binding is the default. This creates confusion for devs from C coming over. Instead I propose that comparing is the default, and binding should be handled by the developer explicitly via assignment. That way, it does not confuse devs, especially in sectors like FFI and C to Python transitions

1 Like

I don’t understand the syntax you’ve shown: match x: case ==y? Maybe I missed something in the match/case feature.

But it is too late to change this about match/case. It would be a breaking change.

Keep in mind: match/case is not “just” a switch statement. It’s a powerful data structure pattern matcher. When used as a simple switch, the behavior can be surprising, but it’s done that way for a reason.

2 Likes

yeah i meant “y if x == y” but the point stands. In many technical languages, comparison is the default, and binding is handled via manual assignment if desired. Its breaking yes, but it can be rolled out carefully. Either that or use some form of opt-in bind toggle

I’m sorry, but this is not going to change, both because it would be breaking, and because match/case isn’t meant to be a switch statement using comparison.

Have you read through the Structural Pattern Matching Tutorial to get a sampling of the full power?

I’ve also encountered this issue. It can be confusing when constant values are outside of an enum and thus won’t trigger the constant matching syntax because it doesn’t include the dot.

I have to do this to match a local constant:

match x:
   case _ if x == y: ...

I don’t mind doing this because it doesn’t come up often. I might use a dict lookup in most cases where I encounter this. Most “switch” statements work best as a dictionary lookup.

Instead of changing the default. I’d propose new explicit syntax based on existing matching syntax:

match x:
    case .y: ...  # matches if x==y, y must be a name
    case =z: ...  # explicit capture, assigns z=x
4 Likes

I also find myself sometimes wishing for case to support looking up names directly in the local or global namespaces instead of interpreting them as capture patterns and requiring me to capture a value into an intermediate variable only to compare it to the variable I actually need with an if guard.

However, I can’t think of an elegant syntax that can offer both capabilitiies of capturing a value into a new variable and binding a name to an existing variable. The proposed syntaxes are definitely not it.

One possible workaround I can offer to the OP is to use a wrapper object that looks up a requested attribute name in the caller’s local or global namespace:

import sys

class Local:
    def __getattr__(self, name):
        return sys._getframe(1).f_locals[name] # f_globals for global namespace
local = Local()

so that:

x = 1
y = 1
match x:
    case local.y:
        print('x == y') # outputs x == y

and that:

class Point:
    __match_args__ = 'x', 'y'
    def __init__(self, x, y):
        self.x = x
        self.y = y
x = 1
match Point(1, 2):
    case Point(local.x, y):
        print(y) # outputs 2
1 Like

Love that proposed syntax. Provides readability and explicitness, no relying on implicit behaviour. Devs should be able to control whether they want binding or comparison. To them, implicit behaviour is a headache

Doesn’t look intuitively readable to me. Dot and equal sign look weird as prefixes of a name. Repurposing symbols are generally frowned upon in Python unless those symbols represent a widely estasblished convention.

That said, I understand where those symbols come from once I think hard about the current syntaxes:

  1. namespace.name is the current syntax for referencing an existing variable, so it makes sense that namespace defaults to the local namespace (and failing that, the global namespace) when namespace is missing.
  2. Class(attribute=name) is the current syntax for capturing a named attribute in a class pattern, so using an absence of Class and attribute for capturing an unnamed value kind of makes sense.

But still, they just don’t look immediately comprehensible to me, so I don’t think I’ll be holding my breath for its acceptance.

2 Likes

For some prior art, Swift and Zig allow case .var with a leading dot (though they use it for enums). So, there is precedent in favor of leading dot syntax.

See Pattern matching on constants that aren't in a namespace (especially sentinels)

I thought I was in that thread! I get confused between the two threads :sweat_smile: