gvanrossum / patma

Pattern Matching
1.03k stars 65 forks source link

Should Sequence patterns allow both (x, y) and [x, y]? #29

Open gvanrossum opened 4 years ago

gvanrossum commented 4 years ago

Sequence patterns aren't quite the same as sequence unpacking assignments (the behavior for iterators and strings is different), but nevertheless we may follow their lead and allow both (x, y) and [x, y].

Or perhaps not: it may be somewhat confusing that case (x, y) matches any sequence, not just tuples.

Tobias-Kohn commented 4 years ago

This is quite a tough one. My first instinct is to say that tuples and lists are two entirely different beasts, but that might be because of my background in Scala.

On the one hand, one of the great strengths of Python is its open protocols, i.e. that anything with __getitem__ and __len__ methods can basically pass as a tuple or list. If we could respect this here, that would actually feel quite Pythonic—even at the expense of all cats being grey.

On the other hand, I wonder how the matching protocoll would actually look like. Say, we want to translate this:

match value:
    case (x, 0, y):
        ...

Do we just go for something like this?

if len(value) == 3:
    x = value.__getitem__(0)
    if value.__getitem__(1) == 0:
        y = value.__getitem__(2)
        ...

But then, what happens if the value in question does not have a len-field? — or a __getitem__ for that matter. What happens if value is a dictionary, where the subsequent __getitem__s might fail although the len-guard in the beginning did not?

Properly checking whether a value can pass as a sequence of a given length without throwing any exceptions or causing side effects might turn out to be rather tricky. In that sense, it could be much simpler to say that (x, 0, y) really means a tuple and nothing else.

gvanrossum commented 4 years ago

Please read the code in patma.py. It's all there, starting with isinstance(target, collections.abc.Sequence).

gvanrossum commented 4 years ago

Specifically, for either [x, 0, y] and (x, 0, y) I would generate exactly the same code, and it would be roughly like this:

if (isinstance(target, collections.abc.Sequence)
        and len(target) == 3
        and target[1] == 0):
    x = target[0]
    y = target[2]
brandtbucher commented 4 years ago

I think we shouldn't allow the tuple syntax. There's no advantage (and several disadvantages) vs. the list syntax, and it leaves the door open for using them differently in the future (iterator patterns, maybe?).

gvanrossum commented 4 years ago

That's okay. Let's be minimalist to start. It'll also remind people that this isn't quite the same as unpacking assignments.

gvanrossum commented 4 years ago

I'd like to reopen this. From https://github.com/gvanrossum/patma/issues/58#issuecomment-640960482:

I just realized that this makes patterns like tuple([1, 2, 3]) work naturally too.

And this is actually an argument for allowing (...) for sequence patterns, since then we could write it as the much more satisfying tuple((1, 2, 3)).

viridia commented 4 years ago

Is there a decision on this? I am ambivalent, I can see the argument either way.

brandtbucher commented 4 years ago

I still think we should hold off, and that the new tuple argument is a bit weak. We could always add it later if people really want it, but we are also keeping it free for other possible meanings as well.

I don't feel very strongly one way or the other, but it feels a bit wasteful to allow them here. We already deviate significantly from unpacking assignment / del syntax, where both forms are interchangeable.

gvanrossum commented 4 years ago

Every time I try something it seems this keeps bugging me. For example in https://github.com/gvanrossum/patma/pull/68/files I really want to write tuples, and using sequence notation for two fields of different types just feels very wrong. And there was the "line 484" in the PEP which I looked at several times without seeing the bug.

I think the argument that we could reserve them for some other feature is also weak -- it would still confuse users (since many examples of sequence tuples do look very similar to tuples, either on the LHS or on the RHS of =).

Can we please just do this?

brandtbucher commented 4 years ago

Okay, I'll add this to my to-do list for the implementation.

brandtbucher commented 4 years ago

Done! Here's the commit if you'd like to review the grammar:

https://github.com/brandtbucher/cpython/pull/2/commits/31301142219270ece0a5c4ff2987a2561b1ed243#diff-63955b386db4e19ea65eba0da76de53d

Delta456 commented 4 years ago

I think its better to use tuple as it is immutable or the user can explicitly tell the type of sequence pattern. Like:

match point:
    case list(0, 0):
        print("Origin")
    case tuple(0, y):
        print(f"Y={y}")
    case list(x, 0):
        print(f"X={x}")
    case tuple(x, y):
        print(f"X={x}, Y={y}")
    case _:
        raise ValueError("Not a point")