classification
Title: Undesired Behavior on `match` using Singleton object
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.10
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Dennis Sweeney, brandtbucher, thepabloaguilar
Priority: normal Keywords:

Created on 2021-07-13 03:34 by thepabloaguilar, last changed 2021-08-04 21:12 by brandtbucher. This issue is now closed.

Messages (6)
msg397380 - (view) Author: Pablo Aguilar (thepabloaguilar) Date: 2021-07-13 03:34
Hey, I'm not sure if this is really a bug but I'd like to show to prevent some undesired behavior!

I'm working in the `match` support for returns (https://github.com/dry-python/returns) library when I saw a behavior similar to the described in `Constant Value Patterns` section on PEP-622 (https://www.python.org/dev/peps/pep-0622/#constant-value-patterns).

A very small and reproducible example:
```python
from typing import Any, ClassVar, Optional


class Maybe:
    empty: ClassVar['Maybe']
    _instance: Optional['Maybe'] = None

    def __new__(cls, *args: Any, **kwargs: Any) -> 'Maybe':
        if cls._instance is None:
            cls._instance = object.__new__(cls)
        return cls._instance


Maybe.empty = Maybe()


if __name__ == '__main__':
    my_maybe = Maybe()
    match my_maybe:
        case Maybe.empty:
            print('FIRST CASE')
        case _:
            print('DEFAULT CASE')
```

The output here is `FIRST CASE`, but if I delete `__new__` method the output is `DEFAULT CASE`.

Is that the correct behavior?

Python version: 3.10.0a7
msg397384 - (view) Author: Dennis Sweeney (Dennis Sweeney) * (Python triager) Date: 2021-07-13 06:16
This code...

    match my_maybe:
        case Maybe.empty:
            print('FIRST CASE')
        case _:
            print('DEFAULT CASE')

... is roughly equivalent to this code:

    if my_maybe == Maybe.empty:
        print('FIRST CASE')
    case _:
        print('DEFAULT CASE')


I don't think this is a bug in the match/case compiler, I think it's simply a matter of how Maybe() == Maybe() evaluates. Since your __new__ code always returns the same object, Maybe() == Maybe() will return True. But by default, each Maybe() call constructs a new instance that won't be equal to any others.
msg397456 - (view) Author: Pablo Aguilar (thepabloaguilar) Date: 2021-07-14 02:25
But, `Maybe.empty` works like a `Literal Pattern` or `Constant Pattern`?
msg397457 - (view) Author: Pablo Aguilar (thepabloaguilar) Date: 2021-07-14 02:31
In fact, I'm worried about how to explain some behaviors.

Look here:
```python
# `Maybe` here is the same class described in the first message
Nothing = Maybe()
Maybe.empty = Nothing


if __name__ == '__main__':
    my_maybe = Maybe()
    match my_maybe:
        case Nothing:
            print('FIRST CASE')
        case _:
            print('DEFAULT CASE')
```

I can't use `Nothing` to match the values even though it's a variable but using `Maybe.empty` it works.
msg397459 - (view) Author: Dennis Sweeney (Dennis Sweeney) * (Python triager) Date: 2021-07-14 03:31
From https://www.python.org/dev/peps/pep-0635/#value-patterns :

"""We therefore only adopted the rule that any dotted name (i.e., attribute access) is to be interpreted as a value pattern, for example HttpStatus.OK above. This precludes, in particular, local variables and global variables defined in the current module from acting as constants."""

So your `case Nothing` example is an irrefutable capture pattern that binds the subject to the name "Nothing", while `case Maybe.empty` is a value pattern.

This is probably a little confusing the first time a person sees it, but it is in fact working in compliance with PEP 634, PEP 635, and PEP 636.
msg398955 - (view) Author: Brandt Bucher (brandtbucher) * (Python committer) Date: 2021-08-04 21:12
Yep, everything is working as intended here. Thanks, Dennis, for taking the time to explain why.

Pablo, if you're still unsure of why your examples behave the way they do, I strongly recommend reading through the official tutorial (PEP 636). It goes through the behavior of each of the different patterns in a very straightforward, easy-to-digest way.
History
Date User Action Args
2021-08-04 21:12:18brandtbuchersetstatus: open -> closed
resolution: not a bug
messages: + msg398955

stage: resolved
2021-07-14 21:55:53pablogsalsetnosy: + brandtbucher
2021-07-14 03:31:38Dennis Sweeneysetmessages: + msg397459
2021-07-14 02:31:59thepabloaguilarsetmessages: + msg397457
2021-07-14 02:25:11thepabloaguilarsetmessages: + msg397456
2021-07-13 06:16:48Dennis Sweeneysetnosy: + Dennis Sweeney
messages: + msg397384
2021-07-13 03:34:48thepabloaguilarcreate