From 7f0661d305ae18f53282566d1683041211a0a0b7 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 4 Jul 2016 23:05:02 +0200 Subject: [PATCH 1/3] Add explanation of singletons in unions to PEP484 --- pep-0484.txt | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/pep-0484.txt b/pep-0484.txt index 6d66df118..c6c16c0f5 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -846,6 +846,51 @@ 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 when ``None`` is 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 could 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`` could not be further subclassed, +the type of variable ``x`` can be statically inferred in all branches +of the above example. + + The ``Any`` type ---------------- From 28f9095199da4e9d2db955d5e6bcf756302afadf Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 5 Jul 2016 08:24:54 +0200 Subject: [PATCH 2/3] Response to comments, added one more example, corrected a typo --- pep-0484.txt | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/pep-0484.txt b/pep-0484.txt index c6c16c0f5..7ac4719ea 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -851,8 +851,8 @@ Support for singleton types in unions ------------------------------------- A singleton instance is frequently used to mark some special condition, -in particular in situations when ``None`` is a valid value for a variable. -Example:: +in particular in situations where ``None`` is also a valid value +for a variable. Example:: _empty = object() @@ -866,7 +866,7 @@ Example:: 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 could be caught statically:: +by the standard library, so that type errors can be caught statically:: from typing import Union from enum import Enum @@ -886,9 +886,24 @@ by the standard library, so that type errors could be caught statically:: else: # At this point typechecker knows that x can only have type int return x * 2 -Since the subclasses of ``Enum`` could not be further subclassed, +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. +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 From 0f55c3114cc506a409ed99d3102cb6d730ded879 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 5 Jul 2016 12:19:50 +0200 Subject: [PATCH 3/3] Implemented style corrections --- pep-0484.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pep-0484.txt b/pep-0484.txt index 7ac4719ea..9c7bdefd8 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -875,7 +875,7 @@ by the standard library, so that type errors can be caught statically:: token = 0 _empty = Empty.token - def func(x: Union[int, None, Empty]=_empty) -> int: + def func(x: Union[int, None, Empty] = _empty) -> int: boom = x * 42 # This fails type check @@ -896,7 +896,7 @@ one value:: timeout = 1 error = 2 - def process(response: Union[str, Reason]='') -> str: + def process(response: Union[str, Reason] = '') -> str: if response is Reason.timeout: return 'TIMEOUT' elif response is Reason.error: