Using Final to signify a singleton?

I was chatting with Guido about how there isn't an easy way to signal that something is a singleton. Concrete example: ```Python _NOTHING = object() class list(builtins.list): def __init__(self, iterable: Iterable | object = _NOTHING, /) -> None: if iterable is not _NOTHING: for item in iterable: ... # Fails to type check. ``` Even if I define a class just for this use-case, it still won't type check since the class that is instantiated once will still not be an iterable. ```Python class _NothingType: pass _NOTHING = _NothingType() ``` I'm not sure how best to handle this case. But while chatting with Guido he suggested one possibility might be to somehow use `Final` to signify that an object is acting as a singleton. Maybe something like: ```Python _NOTHING: Final[object] = object() ``` Then type checkers would know that `_NOTHING` is a singleton. Basically I want a way to write the following without a type failure: ```Python class list(builtins.list): def __init__(self, iterable: Iterable | _NOTHING = _NOTHING, /) -> None: if iterable is not _NOTHING: for item in iterable: ... ```

On Mon, Sep 27, 2021 at 3:45 PM Brett Cannon <brett@python.org> wrote:
I was chatting with Guido about how there isn't an easy way to signal that something is a singleton. Concrete example:
```Python
_NOTHING = object()
class list(builtins.list): def __init__(self, iterable: Iterable | object = _NOTHING, /) -> None: if iterable is not _NOTHING: for item in iterable: ... # Fails to type check. ``` Even if I define a class just for this use-case, it still won't type check since the class that is instantiated once will still not be an iterable.
```Python class _NothingType: pass
_NOTHING = _NothingType() ```
I'm not sure how best to handle this case. But while chatting with Guido he suggested one possibility might be to somehow use `Final` to signify that an object is acting as a singleton. Maybe something like: ```Python _NOTHING: Final[object] = object() ```
Then type checkers would know that `_NOTHING` is a singleton.
Basically I want a way to write the following without a type failure: ```Python class list(builtins.list): def __init__(self, iterable: Iterable | _NOTHING = _NOTHING, /) -> None: if iterable is not _NOTHING: for item in iterable: ... ```
To be clear, I was advocating to use `Literal[_NOTHING]` in the union branch, *in addition* to `_NOTHING: Final = object()`. So the example would become ```py _NOTHING: Final[object] = object() class list(builtins.list): def __init__(self, iterable: Iterable | Literal[_NOTHING] = _NOTHING, /) -> None: if iterable is not _NOTHING: for item in iterable: ... ``` (And no, there's no reason we're using `class list` as the example except that's what prompted Brett to bring this up.) -- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>

Previous discussion: https://github.com/python/typing/issues/689#issuecomment-561425237 https://mail.python.org/archives/list/typing-sig@python.org/thread/NDEJ7UCDP... On Mon, 27 Sept 2021 at 16:01, Guido van Rossum <guido@python.org> wrote:
On Mon, Sep 27, 2021 at 3:45 PM Brett Cannon <brett@python.org> wrote:
I was chatting with Guido about how there isn't an easy way to signal that something is a singleton. Concrete example:
```Python
_NOTHING = object()
class list(builtins.list): def __init__(self, iterable: Iterable | object = _NOTHING, /) -> None: if iterable is not _NOTHING: for item in iterable: ... # Fails to type check. ``` Even if I define a class just for this use-case, it still won't type check since the class that is instantiated once will still not be an iterable.
```Python class _NothingType: pass
_NOTHING = _NothingType() ```
I'm not sure how best to handle this case. But while chatting with Guido he suggested one possibility might be to somehow use `Final` to signify that an object is acting as a singleton. Maybe something like: ```Python _NOTHING: Final[object] = object() ```
Then type checkers would know that `_NOTHING` is a singleton.
Basically I want a way to write the following without a type failure: ```Python class list(builtins.list): def __init__(self, iterable: Iterable | _NOTHING = _NOTHING, /) -> None: if iterable is not _NOTHING: for item in iterable: ... ```
To be clear, I was advocating to use `Literal[_NOTHING]` in the union branch, *in addition* to `_NOTHING: Final = object()`. So the example would become
```py _NOTHING: Final[object] = object()
class list(builtins.list): def __init__(self, iterable: Iterable | Literal[_NOTHING] = _NOTHING, /) -> None: if iterable is not _NOTHING: for item in iterable: ... ```
(And no, there's no reason we're using `class list` as the example except that's what prompted Brett to bring this up.)
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...> _______________________________________________ Typing-sig mailing list -- typing-sig@python.org To unsubscribe send an email to typing-sig-leave@python.org https://mail.python.org/mailman3/lists/typing-sig.python.org/ Member address: hauntsaninja@gmail.com

Brett, is there a reason you don't want to use `None` as a sentinel? It's the one singleton object whose "singleton-ness" is enforced by the runtime. Plus, all type checkers already have intimate knowledge of it, so `is not None` works for type narrowing. Here's another idea to consider. Would it make sense to expose a new metaclass from stdlib (let's call it `SingletonMeta`) that enforces the singleton-ness of a class? Any class based on this metaclass could be instantiated only once, and an attempt to instantiate it a second time would result in a runtime exception. Type checkers could then assume that all instances of that class are the same object, and the `is X` and `is not X` type narrowing logic could take advantage of that knowledge. -Eric -- Eric Traut Contributor to pyright & pylance Microsoft Corp.

The singleton is an accepted pattern in Python, and there are sometimes good reasons for it. (Even if that reason is not obvious in the current example.) If we're going the metaclass route, why not just use enums? class Nothing: NOTHING = 'nothing' def f(arg: Iterable[int] | Nothing = Nothing.NOTHING) -> int: ... On Mon, Sep 27, 2021 at 6:17 PM Eric Traut <eric@traut.com> wrote:
Brett, is there a reason you don't want to use `None` as a sentinel? It's the one singleton object whose "singleton-ness" is enforced by the runtime. Plus, all type checkers already have intimate knowledge of it, so `is not None` works for type narrowing.
Here's another idea to consider. Would it make sense to expose a new metaclass from stdlib (let's call it `SingletonMeta`) that enforces the singleton-ness of a class? Any class based on this metaclass could be instantiated only once, and an attempt to instantiate it a second time would result in a runtime exception. Type checkers could then assume that all instances of that class are the same object, and the `is X` and `is not X` type narrowing logic could take advantage of that knowledge.
-Eric
-- Eric Traut Contributor to pyright & pylance Microsoft Corp. _______________________________________________ Typing-sig mailing list -- typing-sig@python.org To unsubscribe send an email to typing-sig-leave@python.org https://mail.python.org/mailman3/lists/typing-sig.python.org/ Member address: guido@python.org
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>

On Mon, Sep 27, 2021 at 7:18 PM Eric Traut <eric@traut.com> wrote:
Brett, is there a reason you don't want to use `None` as a sentinel?
Can’t speak for Brett’s specific case, but I’ve more than once had cases where None could be a valid user-provided value for an argument, and thus a function-specific sentinel was needed. This pattern used to be pretty simple with `MISSING = object()`; typing makes it more complicated. Carl

There has been a lot of detailed discussion recently on discuss.python.org regarding a proposed PEP (PEP 661) to provide a better solution to the issue of sentinel values in Python. Much of this conversation seems to be duplicating points that have already been discussed in that thread: https://discuss.python.org/t/pep-661-sentinel-values/9126.Notably, the issue of how to approach type-hinting sentinel values is one of the key points of discussion in the thread. Alex -------- Original message --------From: Carl Meyer <carl@oddbird.net> Date: 28/09/2021 04:22 (GMT+00:00) To: Eric Traut <eric@traut.com> Cc: typing-sig@python.org Subject: [Typing-sig] Re: Using Final to signify a singleton? On Mon, Sep 27, 2021 at 7:18 PM Eric Traut <eric@traut.com> wrote:Brett, is there a reason you don't want to use `None` as a sentinel? Can’t speak for Brett’s specific case, but I’ve more than once had cases where None could be a valid user-provided value for an argument, and thus a function-specific sentinel was needed. This pattern used to be pretty simple with `MISSING = object()`; typing makes it more complicated. Carl

Thanks for the pointer. A quick look didn't find anything in the PEP (661) about types, and the thread you linked to is too long to read in its entirety. Can someone summarize what they ended up deciding? On Tue, Sep 28, 2021 at 8:41 AM Alex Waygood <alex.waygood@gmail.com> wrote:
There has been a lot of detailed discussion recently on discuss.python.org regarding a proposed PEP (PEP 661) to provide a better solution to the issue of sentinel values in Python. Much of this conversation seems to be duplicating points that have already been discussed in that thread: https://discuss.python.org/t/pep-661-sentinel-values/9126.
Notably, the issue of how to approach type-hinting sentinel values is one of the key points of discussion in the thread.
Alex
-------- Original message -------- From: Carl Meyer <carl@oddbird.net> Date: 28/09/2021 04:22 (GMT+00:00) To: Eric Traut <eric@traut.com> Cc: typing-sig@python.org Subject: [Typing-sig] Re: Using Final to signify a singleton?
On Mon, Sep 27, 2021 at 7:18 PM Eric Traut <eric@traut.com> wrote:
Brett, is there a reason you don't want to use `None` as a sentinel?
Can’t speak for Brett’s specific case, but I’ve more than once had cases where None could be a valid user-provided value for an argument, and thus a function-specific sentinel was needed. This pattern used to be pretty simple with `MISSING = object()`; typing makes it more complicated.
Carl _______________________________________________ Typing-sig mailing list -- typing-sig@python.org To unsubscribe send an email to typing-sig-leave@python.org https://mail.python.org/mailman3/lists/typing-sig.python.org/ Member address: guido@python.org
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>

Hi, I'm the author of PEP 661: Sentinel Values. Here's a direct link to the PEP <https://www.python.org/dev/peps/pep-0661/>. There was indeed a significant thread about this here on typing-sig <https://mail.python.org/archives/list/typing-sig@python.org/thread/NDEJ7UCDP...> (also mentioned in a previous message in this thread). The current status of the PEP is that it's stalled due to me not finishing it. I got caught up in the typing discussion, largely due to not having much previous experience with Python typing, and failed to come to a clear conclusion of what a good solution would be.
Even if I define a class just for this use-case, it still won't type check since the class that is instantiated once will still not be an iterable.
I wasn't aware that this could be an issue. If you could explain in more detail what the problem is, that could be helpful. - Tal Einat On Tue, Sep 28, 2021 at 8:07 PM Guido van Rossum <guido@python.org> wrote:
Thanks for the pointer. A quick look didn't find anything in the PEP (661) about types, and the thread you linked to is too long to read in its entirety. Can someone summarize what they ended up deciding?
On Tue, Sep 28, 2021 at 8:41 AM Alex Waygood <alex.waygood@gmail.com> wrote:
There has been a lot of detailed discussion recently on discuss.python.org regarding a proposed PEP (PEP 661) to provide a better solution to the issue of sentinel values in Python. Much of this conversation seems to be duplicating points that have already been discussed in that thread: https://discuss.python.org/t/pep-661-sentinel-values/9126.
Notably, the issue of how to approach type-hinting sentinel values is one of the key points of discussion in the thread.
Alex
-------- Original message -------- From: Carl Meyer <carl@oddbird.net> Date: 28/09/2021 04:22 (GMT+00:00) To: Eric Traut <eric@traut.com> Cc: typing-sig@python.org Subject: [Typing-sig] Re: Using Final to signify a singleton?
On Mon, Sep 27, 2021 at 7:18 PM Eric Traut <eric@traut.com> wrote:
Brett, is there a reason you don't want to use `None` as a sentinel?
Can’t speak for Brett’s specific case, but I’ve more than once had cases where None could be a valid user-provided value for an argument, and thus a function-specific sentinel was needed. This pattern used to be pretty simple with `MISSING = object()`; typing makes it more complicated.
Carl

On Tue, Sep 28, 2021 at 10:27 AM Tal Einat <taleinat@gmail.com> wrote:
I'm the author of PEP 661: Sentinel Values. Here's a direct link to the PEP <https://www.python.org/dev/peps/pep-0661/>.
There was indeed a significant thread about this here on typing-sig <https://mail.python.org/archives/list/typing-sig@python.org/thread/NDEJ7UCDP...> (also mentioned in a previous message in this thread).
Oh, I see, and I participated. Sorry for being so fuzzy-brained.
The current status of the PEP is that it's stalled due to me not finishing it. I got caught up in the typing discussion, largely due to not having much previous experience with Python typing, and failed to come to a clear conclusion of what a good solution would be.
I think you've been given all the options in the thread, and you just have to pick one. The best way to find out what mypy currently supports is to install it and try it out on some test files (make sure to run `mypy --strict`). -- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>

On Tue, Sep 28, 2021 at 10:10 PM Guido van Rossum <guido@python.org> wrote:
On Tue, Sep 28, 2021 at 10:27 AM Tal Einat <taleinat@gmail.com> wrote:
The current status of the PEP is that it's stalled due to me not finishing it. I got caught up in the typing discussion, largely due to not having much previous experience with Python typing, and failed to come to a clear conclusion of what a good solution would be.
I think you've been given all the options in the thread, and you just have to pick one. The best way to find out what mypy currently supports is to install it and try it out on some test files (make sure to run `mypy --strict`).
Indeed, that's my impression too. After going through the discussions again, I'm going to go with using Literal[SentinelValue]. I'll add that to the PEP soon. - Tal

+1 On Wed, Sep 29, 2021 at 4:43 AM Tal Einat <taleinat@gmail.com> wrote:
On Tue, Sep 28, 2021 at 10:10 PM Guido van Rossum <guido@python.org> wrote:
On Tue, Sep 28, 2021 at 10:27 AM Tal Einat <taleinat@gmail.com> wrote:
The current status of the PEP is that it's stalled due to me not finishing it. I got caught up in the typing discussion, largely due to not having much previous experience with Python typing, and failed to come to a clear conclusion of what a good solution would be.
I think you've been given all the options in the thread, and you just have to pick one. The best way to find out what mypy currently supports is to install it and try it out on some test files (make sure to run `mypy --strict`).
Indeed, that's my impression too.
After going through the discussions again, I'm going to go with using Literal[SentinelValue]. I'll add that to the PEP soon.
- Tal
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>

On Wed, Sep 29, 2021 at 2:43 PM Tal Einat <taleinat@gmail.com> wrote:
On Tue, Sep 28, 2021 at 10:10 PM Guido van Rossum <guido@python.org> wrote:
On Tue, Sep 28, 2021 at 10:27 AM Tal Einat <taleinat@gmail.com> wrote:
The current status of the PEP is that it's stalled due to me not finishing it. I got caught up in the typing discussion, largely due to not having much previous experience with Python typing, and failed to come to a clear conclusion of what a good solution would be.
I think you've been given all the options in the thread, and you just have to pick one. The best way to find out what mypy currently supports is to install it and try it out on some test files (make sure to run `mypy --strict`).
Indeed, that's my impression too.
After going through the discussions again, I'm going to go with using Literal[SentinelValue]. I'll add that to the PEP soon.
The PEP <https://www.python.org/dev/peps/pep-0661/> has been updated, with the Specification section now saying the type annotations should use Literal[]. (Note that the page at that link may take several hours to show the latest changes.) - Tal
participants (7)
-
Alex Waygood
-
Brett Cannon
-
Carl Meyer
-
Eric Traut
-
Guido van Rossum
-
Shantanu Jain
-
Tal Einat