grscheller.untyped.nothing
Singleton object representing a missing value
- this module was an attempt to give Python a "bottom" type
- a true bottom type has no instantiated values
- nothing: Nothing is instantiated as a singleton
- types like
~T|Noneand~T|()are a poor man's Maybe Monad- both
Noneand()make for lousy "bottom" types - both accept few methods
Nonehas no length()at least is iterable
- both must constantly be checked for when returned from functions
- it is Pythonic for developers to use
Noneand()as sentinels
- both
1# Copyright 2023-2024 Geoffrey R. Scheller 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""#### Singleton object representing a missing value 16 17* this module was an attempt to give Python a "bottom" type 18 * a true bottom type has no instantiated values 19 * nothing: Nothing is instantiated as a singleton 20* types like `~T|None` and `~T|()` are a poor man's Maybe Monad 21 * both `None` and `()` make for lousy "bottom" types 22 * both accept few methods 23 * `None` has no length 24 * `()` at least is iterable 25 * both must constantly be checked for when returned from functions 26 * it is Pythonic for developers to use `None` and `()` as sentinels 27""" 28 29from __future__ import annotations 30 31__all__ = [ 'Nothing', 'nothing' ] 32 33from typing import Any, Callable, Final, Iterator 34 35_Nothing_Nada = tuple[None, tuple[None, tuple[None, tuple[None, None]]]] 36_nothing_nada: _Nothing_Nada = None, (None, (None, (None, None))) 37 38class Nothing(): 39 """ 40 ##### Singleton semantically represents a missing value. 41 42 * singleton nothing: Nothing = Nothing() represents a non-existent value 43 * makes for a better "bottom type" than either `None` or `()` 44 * returns itself for arbitrary method calls 45 * returns itself if called as a Callable with arbitrary arguments 46 * interpreted as an empty container by standard Python functions 47 * for comparison operators return `False` to "stop an action" 48 * behaves like IEEE Float NAN's with comparison operators 49 * avoid using else clauses when comparing ~T|Nothing values 50 * when on left: 51 * always returns `False` 52 * when on right: 53 * relies on the "std convention" of returning `False` if types differ 54 * avoid using `~T|Nothing` values on right side of comparisons 55 * especially for numerical comparisons 56 """ 57 __slots__ = () 58 59 def __new__(cls) -> Nothing: 60 if not hasattr(cls, 'instance'): 61 cls.instance = super(Nothing, cls).__new__(cls) 62 return cls.instance 63 64 def __iter__(self) -> Iterator[Any]: 65 return iter(()) 66 67 def __repr__(self) -> str: 68 return 'nothing' 69 70 def __bool__(self) -> bool: 71 return False 72 73 def __len__(self) -> int: 74 return 0 75 76 def __add__(self, right: Any) -> Any: 77 return Nothing() 78 79 def __radd__(self, left: Any) -> Any: 80 return Nothing() 81 82 def __mul__(self, right: Any) -> Any: 83 return Nothing() 84 85 def __rmul__(self, left: Any) -> Any: 86 return Nothing() 87 88 def __ge__(self, right: Any) -> bool: 89 return False 90 91 def __gt__(self, right: Any) -> bool: 92 return False 93 94 def __le__(self, right: Any) -> bool: 95 return False 96 97 def __lt__(self, right: Any) -> bool: 98 return False 99 100 def __eq__(self, right: Any) -> bool: 101 return False 102 103 def __ne__(self, right: Any) -> bool: 104 return False 105 106 def __getitem__(self, index: int|slice) -> Any: 107 return Nothing() 108 109 def __setitem__(self, index: int|slice, item: Any) -> None: 110 return 111 112 def __call__(*args: Any, **kwargs: Any) -> Any: 113 return Nothing() 114 115 # def __getattr__(self, name: str) -> Callable[[Any], Any]: 116 # """Comment out for doc generation, pdoc gags on this method.""" 117 # def method(*args: Any, **kwargs: Any) -> Any: 118 # return Nothing() 119 # return method 120 121 def get(self, alt: Any=_nothing_nada) -> Any: 122 """ 123 ##### Get an alternate value, defaults to Nothing(). 124 """ 125 if alt == _nothing_nada: 126 return Nothing() 127 else: 128 return alt 129 130nothing = Nothing()
class
Nothing:
39class Nothing(): 40 """ 41 ##### Singleton semantically represents a missing value. 42 43 * singleton nothing: Nothing = Nothing() represents a non-existent value 44 * makes for a better "bottom type" than either `None` or `()` 45 * returns itself for arbitrary method calls 46 * returns itself if called as a Callable with arbitrary arguments 47 * interpreted as an empty container by standard Python functions 48 * for comparison operators return `False` to "stop an action" 49 * behaves like IEEE Float NAN's with comparison operators 50 * avoid using else clauses when comparing ~T|Nothing values 51 * when on left: 52 * always returns `False` 53 * when on right: 54 * relies on the "std convention" of returning `False` if types differ 55 * avoid using `~T|Nothing` values on right side of comparisons 56 * especially for numerical comparisons 57 """ 58 __slots__ = () 59 60 def __new__(cls) -> Nothing: 61 if not hasattr(cls, 'instance'): 62 cls.instance = super(Nothing, cls).__new__(cls) 63 return cls.instance 64 65 def __iter__(self) -> Iterator[Any]: 66 return iter(()) 67 68 def __repr__(self) -> str: 69 return 'nothing' 70 71 def __bool__(self) -> bool: 72 return False 73 74 def __len__(self) -> int: 75 return 0 76 77 def __add__(self, right: Any) -> Any: 78 return Nothing() 79 80 def __radd__(self, left: Any) -> Any: 81 return Nothing() 82 83 def __mul__(self, right: Any) -> Any: 84 return Nothing() 85 86 def __rmul__(self, left: Any) -> Any: 87 return Nothing() 88 89 def __ge__(self, right: Any) -> bool: 90 return False 91 92 def __gt__(self, right: Any) -> bool: 93 return False 94 95 def __le__(self, right: Any) -> bool: 96 return False 97 98 def __lt__(self, right: Any) -> bool: 99 return False 100 101 def __eq__(self, right: Any) -> bool: 102 return False 103 104 def __ne__(self, right: Any) -> bool: 105 return False 106 107 def __getitem__(self, index: int|slice) -> Any: 108 return Nothing() 109 110 def __setitem__(self, index: int|slice, item: Any) -> None: 111 return 112 113 def __call__(*args: Any, **kwargs: Any) -> Any: 114 return Nothing() 115 116 # def __getattr__(self, name: str) -> Callable[[Any], Any]: 117 # """Comment out for doc generation, pdoc gags on this method.""" 118 # def method(*args: Any, **kwargs: Any) -> Any: 119 # return Nothing() 120 # return method 121 122 def get(self, alt: Any=_nothing_nada) -> Any: 123 """ 124 ##### Get an alternate value, defaults to Nothing(). 125 """ 126 if alt == _nothing_nada: 127 return Nothing() 128 else: 129 return alt
Singleton semantically represents a missing value.
- singleton nothing: Nothing = Nothing() represents a non-existent value
- makes for a better "bottom type" than either
Noneor()- returns itself for arbitrary method calls
- returns itself if called as a Callable with arbitrary arguments
- interpreted as an empty container by standard Python functions
- for comparison operators return
Falseto "stop an action"- behaves like IEEE Float NAN's with comparison operators
- avoid using else clauses when comparing ~T|Nothing values
- when on left:
- always returns
False
- always returns
- when on right:
- relies on the "std convention" of returning
Falseif types differ - avoid using
~T|Nothingvalues on right side of comparisons- especially for numerical comparisons
- relies on the "std convention" of returning
def
get(self, alt: Any = (None, (None, (None, (None, None))))) -> Any:
122 def get(self, alt: Any=_nothing_nada) -> Any: 123 """ 124 ##### Get an alternate value, defaults to Nothing(). 125 """ 126 if alt == _nothing_nada: 127 return Nothing() 128 else: 129 return alt
Get an alternate value, defaults to Nothing().
nothing =
nothing