diff --git a/pep-0484.txt b/pep-0484.txt index 6d66df11..9c7bdefd 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -846,6 +846,66 @@ This is equivalent to:: def handle_employee(e: Optional[Employee] = None) -> None: ... + +Support for singleton types in unions +------------------------------------- + +A singleton instance is frequently used to mark some special condition, +in particular in situations where ``None`` is also a valid value +for a variable. Example:: + + _empty = object() + + def func(x=_empty): + if x is _empty: # default argument value + return 0 + elif x is None: # argument was provided and it's None + return 1 + else: + return x * 2 + +To allow precise typing in such situations, the user should use +the ``Union`` type in conjuction with the ``enum.Enum`` class provided +by the standard library, so that type errors can be caught statically:: + + from typing import Union + from enum import Enum + + class Empty(Enum): + token = 0 + _empty = Empty.token + + def func(x: Union[int, None, Empty] = _empty) -> int: + + boom = x * 42 # This fails type check + + if x is _empty: + return 0 + elif x is None: + return 1 + else: # At this point typechecker knows that x can only have type int + return x * 2 + +Since the subclasses of ``Enum`` cannot be further subclassed, +the type of variable ``x`` can be statically inferred in all branches +of the above example. The same approach is applicable if more than one +singleton object is needed: one can use an enumeration that has more than +one value:: + + class Reason(Enum): + timeout = 1 + error = 2 + + def process(response: Union[str, Reason] = '') -> str: + if response is Reason.timeout: + return 'TIMEOUT' + elif response is Reason.error: + return 'ERROR' + else: + # response can be only str, all other possible values exhausted + return 'PROCESSED: ' + response + + The ``Any`` type ----------------