patma icon indicating copy to clipboard operation
patma copied to clipboard

'continue' and 'break' in case blocks

Open gvanrossum opened this issue 5 years ago • 9 comments

@brandtbucher proposed to support continue in case blocks, with the semantics of skipping forward to the next case. This could be used instead of guards (#3). E.g.

match [randint(1, 6), randint(1, 6)]:
    case [a, b]:
        if a != b: continue
        print("Pair:", a)
    ...

If we were to support this we could also support break, which would exit the match statement.

Note that continue could also be used to emulate C's "fall-through" behavior (which should definitely not be the default):

match x:
    case Foo(a, b): continue
    case Bar(a, b): print("Either a Foo or a Bar", a, b)

gvanrossum avatar Apr 12 '20 05:04 gvanrossum

continue makes more sense to me as "continue trying more matches". Blindly jumping into the next match block causes all sorts of problems with missing variable extractions, etc. Besides, it's nothing that couldn't already be done with union patterns.

I also like using break this way, and the two are a natural pair. It seems very symmetric that there's an implicit break at the end of a block, similar to an implicit return at the end of a function.

brandtbucher avatar Apr 13 '20 16:04 brandtbucher

It is also ironically echoing C's use of break in switch statements. :-)

gvanrossum avatar Apr 13 '20 19:04 gvanrossum

There is one downside to giving continue and break in match statement special meaning: there is a common code pattern where one loops over some values and has an if ... elif ... else on the value. There are two problems if some of the branches has continue or break statements:

  • When one refactor such if ... elif ... else into a match statement, those continue and break suddenly change meaning (in a somewhat subtle way).
  • Even when one is well aware about semantics of continue and break in match statements it will be awkward to achieve the desired semantics in a loop.

Thinking about my own experience, I wanted to break out of an if ... elif ... else chain quite rarely, while I wanted to break or continue a loop from such chain quite often. Also I think that most use cases for proposed continue semantics can be achieved using guards or assignment expression or | patterns.

Together, I think this may be a strong argument in favor of making continue and break target the enclosing loop rather than the match statement itself.

ilevkivskyi avatar May 16 '20 12:05 ilevkivskyi

I agree.

gvanrossum avatar May 16 '20 15:05 gvanrossum

One aspect of break and continue that I really do not like is that it forces the individual cases into an order. Although this is clearly beyond the scope of what we are currently doing in Python, most languages with pattern matching compile it down to something like a finite state machine. That is, a simple example like this:

match x:
    case foo(2): ...
    case foo(3): ...
    case bar(a): ...

is actually compiled down to hierarchical code like this:

if isinstance(x, foo):
    if foo.arg == 2: ...
    elif foo.arg == 3: ...
elif isinstance(x, bar):
    ....

Introducing break and continue as operators on the match statement would make such optimisations much harder. Yes, I know that this would be nearly impossible to do with our proposal in Python, but I still feel that break and continue do not fit the overall notion of pattern matching.

Anyway, I also fully agree with that match statements are more likely to contain a break or continue to affect a surrounding loop, as Ivan noted. In fact, I first considered using a for loop for my own implementation of case as it seems to be almost ideally suited for that purpose (the iterator can return either zero or one element, which would be a tuple with the values for variables to bind). However, the fact that such a for loop would capture any breaks and continues made me look for a different way and abandon that idea.

So, long story short, I would not include break and continue as part of pattern matching.

Tobias-Kohn avatar May 22 '20 12:05 Tobias-Kohn

@brandtbucher -- any final words about this? Otherwise I propose to label this as "rejected".

gvanrossum avatar May 22 '20 16:05 gvanrossum

Nope, great points have been made. I agree that the drawbacks outweigh the benefits.

brandtbucher avatar May 22 '20 16:05 brandtbucher

Let's keep rejected issues open though. I like to have the full discussion visible.

gvanrossum avatar May 22 '20 21:05 gvanrossum

Agreed - a rejected issue is not 'closed' because there is still work to be done - writing up the reason why it was rejected.

(If an issue had no impact at all on the PEP, then I suppose theoretically we could close it.)

viridia avatar May 22 '20 23:05 viridia