gvanrossum / patma

Pattern Matching
1.02k stars 64 forks source link

Range matching patterns? #12

Open gvanrossum opened 4 years ago

gvanrossum commented 4 years ago

Some languages have range cases, so you could write e.g. case 1...6: print("Dice"). This seems tricky, and quite specialized. Let's not do this?

brandtbucher commented 4 years ago

This would be easy enough with an In(container) object, or a guard. A perhaps more magical option could be to give range a __match__ method with the same semantics.

I'm not a huge fan of using Ellipsis like this, though. I think it works better for other languages that already have literal range constructs with the same syntax.

gvanrossum commented 4 years ago

That's a neat solution. +1 (to either In(container) or giving range() a __match__ method).

viridia commented 4 years ago

Is using range() as a match expression ambiguous? Does range(3) match 2 or (0, 1, 2)?

In some regards a slice operator makes more sense as a matching expression, but I don't think that works syntactically.

gvanrossum commented 4 years ago

I'm sure range(3) matches (0, 1, 2) -- when I evaluate list(range(3)) I get [0, 1, 2] and basically everybody knows that.

Tobias-Kohn commented 4 years ago

Let me perhaps give another reason why case 1...6 might not bring quite as much as we would hope for. I actually loved this syntax in Pascal. But my main use case was typically character ranges, not integers (I still find syntax like case '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' rather clumsy). However, since Python has no character type (only strings) in the first place, '0'...'9' is simply not feasible, anyway.

Range checks of that form are probably better and quite adequately expressed through classic if-elif-chains, anyway.

Syntax like case 1...6 makes IMHO most sense if you have fast jump tables in mind, where the compiler can check that the entire (numerical) range is covered, and then quickly jump to a specific target based on its value. This is quite a different beast than the structural patterns we have in mind here, and should thus probably better be a non-goal.

Finally, case range(7) seems to express something quite different than case 0...6. With range(7) I would expect the matching value either be a range-object or the full sequence [0, 1, 2, .., 6]. In the case of case 0...7 I would expect any number within that range to match, more along the lines of 0 <= x <= 7.

gvanrossum commented 4 years ago

Agreed -- unless there are strong objections, let's mark this one as "rejected".

Another reason against case 1...6 is that there would be endless debate/confusion about whether it represents a half-open interval (e.g. is it like range(1, 6) or like range(1, 7)), and what to do if the target is a float.

Speaking of floats, syntactically 1...6 would cause problems because the tokenizer would scan it it as 1. . .6.

(However, I don't think that the character range arguments holds water -- we could always declare that it would have to be a string containing a single unicode character. We do this for ord() for example -- another homage to Pascal. :-)

thautwarm commented 4 years ago

Yes, implementing range patterns with dedicated syntax is a waste. We can implement this with current protocol.

gvanrossum commented 4 years ago

But we should not. I have seen your hack and think it is too obfuscated.

thautwarm commented 4 years ago

If you can support parameterised patterns in some way, it won't be obfuscated at all.