Skip to content
This repository has been archived by the owner on Nov 21, 2022. It is now read-only.

'continue' and 'break' in case blocks #9

Open
gvanrossum opened this issue Apr 12, 2020 · 9 comments
Open

'continue' and 'break' in case blocks #9

gvanrossum opened this issue Apr 12, 2020 · 9 comments
Assignees
Labels
fully pepped Issues that have been fully documented in the PEP rejected A rejected idea

Comments

@gvanrossum
Copy link
Owner

@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 gvanrossum mentioned this issue Apr 12, 2020
@gvanrossum gvanrossum changed the title continue and break in case blocks 'continue' and 'break' in case blocks Apr 12, 2020
@brandtbucher
Copy link
Collaborator

brandtbucher commented Apr 13, 2020

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.

@gvanrossum
Copy link
Owner Author

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

@ilevkivskyi
Copy link
Collaborator

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.

@gvanrossum
Copy link
Owner Author

I agree.

@Tobias-Kohn
Copy link
Collaborator

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.

@gvanrossum
Copy link
Owner Author

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

@brandtbucher
Copy link
Collaborator

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

@brandtbucher brandtbucher added the rejected A rejected idea label May 22, 2020
@gvanrossum
Copy link
Owner Author

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

@viridia
Copy link
Collaborator

viridia commented May 22, 2020

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.)

@gvanrossum gvanrossum added the needs more pep An issue which needs to be documented in the PEP label Jun 3, 2020
@viridia viridia self-assigned this Jun 6, 2020
@viridia viridia added fully pepped Issues that have been fully documented in the PEP and removed needs more pep An issue which needs to be documented in the PEP labels Jun 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
fully pepped Issues that have been fully documented in the PEP rejected A rejected idea
Projects
None yet
Development

No branches or pull requests

5 participants