Use "case else" instead of "case _" in match statement

UPD: Probably it not worth it.

Rationale

  1. Wildcard pattern case _ treats legal variable name _ specially (soft keyword).
  2. Syntax case _ has readability issues. It would be much readable if keyword else appear somewhere.

Proposed syntax

  • case else

with if guard denied.

Wildcard pattern case _ can still be used.

This syntax highlighting of else keyword will add to its readability. Compare

match expr:
    case pattern1:
        ...
    case _:        # now
        ...
    case else:     # option 1
        ...
    else:          # option 2
        ...

Alternatives

else: branch was considered and rejected in PEP 635 (see below). Should this decision be revisited?

case: without pattern or keyword. Looks confusing.

Another option could be case ... syntax using Ellipsis, but this will conflict with (theoretically) possible situation when Ellipsis needs to be matched.

Discussion

I haven’t found any mentions of the proposed syntax in PEPs introducing Structural pattern matching: PEP 634, 635, 636.

In PEP 635 a separate match branch else is considered and rejected for a reason:

Else blocks. A case block without a guard whose pattern is a single wildcard (i.e., case _: ) accepts any subject without binding it to a variable or performing any other operation. It is thus semantically equivalent to else: , if it were supported. However, adding such an else block to the match statement syntax would not remove the need for the wildcard pattern in other contexts. Another argument against this is that there would be two plausible indentation levels for an else block: aligned with case or aligned with match . The authors have found it quite contentious which indentation level to prefer.

Right now I use readability trick case _else, but my choice is voluntary, lacks proper syntax highlighting, and _else variable is still never used.

Possible drawbacks

I don’t see any. Sequential keywords are already used in e.g. async def (… with, … for).

The Question

Will this proposal make coding experience smoother?

  • I would vote for case else:
  • I would vote for else:
  • I’m fine with case _
  • I agree that the problem exists, but don’t like the solution
0 voters
1 Like

Is that really true?

>>> match 1:
...     case _:
...         print(_)
...         
Traceback (most recent call last):
  File "<python-input-0>", line 3, in <module>
    print(_)
          ^
NameError: name '_' is not defined
>>> 
3 Likes

@rosuav you are right. I should have written “looks idiomatic”.

Maybe my whole statement is subjective, because using _ is common practice in ignoring values. I’d better remove it.

This also means, though, that it isn’t being MISused as an else branch. There’s no “value of _ variable” to be rarely used. So what you’re asking for is a syntax that has the exact same behaviour as case _: but uses the else keyword instead. I don’t think there’s a very compelling argument for adding a duplicate way of spelling the same thing here.

3 Likes

But _ is special in regard to variable names. This won’t work unless a different name is used:

case _ as _:  # <-- SyntaxError: cannot use '_' as a target
    print(_)
1 Like

Nin uses else: instead of (the equivalent of) case _:, not case else: such you are suggesting.

IMO case else reads terribly.

Just else: as an alias for case _: is an ok idea and reads better.

2 Likes

This makes things even worse. Otherwise legal variable name _ has a special (keyword?) meaning in the case of match statement.

Thank you, I missed this already mentioned alternative, added it to proposal.

I don’t mind case _: or case else:. I would find else: disruptive, probably. I scan for all the cases by scanning for the case keyword.

If we’re discussing placeholders, I wonder whether we could actually upgrade _ into being a proper keyword, outside of match statements too. Probably would be problematic because it could be a breaking change where people have used a value assigned to _. But it could be cool for the sake of things like GitHub - chrisgrimm/better_partial.

1 Like

Actually, there is one more option — use naked case: branch.

Right off the bat it would break functionality of the repl no? It’s been a hot minute since I’ve plain python repl but in ipython _ takes on the value of the last returned value and allows things like

def foo(a):
    return a

foo(1)
a = _
print(a)

How does the default case of match-case work if _ is already assigned to something?

In [1]: _ = 5
In [2]: match 1:
   ...:     case _:
   ...:         print(_)
   ...: 
5

The _ in case _ does not name a variable.

2 Likes
_ = 4

match 3:
    case _:
        print(f"Matched {_=}")
--->
"Matched _=4"

Besides which, I don’t think there’d be a conflict. Assigning to _ would be meaningless. So _ would be free to always carry the value of the last returned value.

2 Likes

This does not look clear to me.

If you want the value of the catch-all case, use a name:

_ = 4

match 3:
    case whatever:
        print(f"Matched {whatever=}")
--->
"Matched whatever=3"
2 Likes

Thank you! I meant it’s error prone. I’m using Python since 2.4 but realized that _ is not a variable in case _ only in this thread.

I typically use

case wrong_data:
     raise ValueError(wrong_data)

as last entry in match statements.

I’m not a fan of “case _”, since it’s not easy to make sense of, unless you know that “_” is often used as throw-away variable for sequence unpacking in Python.

5 Likes

Note this issue was discussed at great depth during the original discussions about adding match/case. case _ was what we landed on, and unless something has changed since then I suggest we stick with it.

11 Likes

Let’s see if this change gets support from community. At least one thing changed since the decision was made: a few years of real evaluation have passed.