Title: PEP 604 NewType
Type: Stage:
Components: Versions: Python 3.11, Python 3.10
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Jelle Zijlstra, domdfcoding, gvanrossum, joperez, kj, levkivskyi, uriyyo
Priority: normal Keywords:

Created on 2021-06-08 20:21 by joperez, last changed 2021-06-10 13:55 by Jelle Zijlstra.

Messages (4)
msg395359 - (view) Author: Joseph Perez (joperez) * Date: 2021-06-08 20:21
`typing.NewType` doesn't support PEP 604.

>>> import typing
>>> A = typing.NewType("A", int)
>>> B = typing.NewType("B", str)
>>> A | B
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'function' and 'function'
msg395512 - (view) Author: Dominic Davis-Foster (domdfcoding) * Date: 2021-06-10 07:36
It is possible to implement NewType to return a class rather than a function as it does currently. However, the class version uses substantially more memory (1072 bytes vs 144 bytes with sys.getsizeof) and NewType is supposed to have almost no runtime overhead.
msg395519 - (view) Author: Yurii Karabas (uriyyo) * Date: 2021-06-10 11:29
What about to return callable object instead of function from a `typing.NewType`?

It will look something like this:
class _NewType:
    __slots__ = ('__name__', '__supertype__')

    def __init__(self, name, supertype):
        self.__name__ = name
        self.__supertype__ = supertype

    def __call__(self, val):
        return val

    def __or__(self, other):
        return Union[self, other]

    def __ror__(self, other):
        return Union[other, self]

def NewType(name, tp):
    """NewType creates simple unique types with almost zero
    runtime overhead. NewType(name, tp) is considered a subtype of tp
    by static type checkers. At runtime, NewType(name, tp) returns
    a dummy callable object that simply returns its argument. Usage::

        UserId = NewType('UserId', int)

        def name_by_id(user_id: UserId) -> str:

        UserId('user')          # Fails type check

        name_by_id(42)          # Fails type check
        name_by_id(UserId(42))  # OK

        num = UserId(5) + 1     # type: int
    return _NewType(name, tp)

With such implementation `__or__` will be available for a NewType and actually because of __slots__ size of object will be 48 bytes (with sys.getsizeof) which is less than current 144 bytes (if memory is important).
msg395538 - (view) Author: Jelle Zijlstra (Jelle Zijlstra) * (Python triager) Date: 2021-06-10 13:55 has some previous discussion of implementing NewType as a class (motivated by __repr__), including benchmarks.
Date User Action Args
2021-06-10 13:55:37Jelle Zijlstrasetmessages: + msg395538
2021-06-10 13:35:30kjsetnosy: + gvanrossum, levkivskyi, Jelle Zijlstra, kj

versions: + Python 3.11
2021-06-10 11:29:35uriyyosetnosy: + uriyyo
messages: + msg395519
2021-06-10 07:36:45domdfcodingsetnosy: + domdfcoding
messages: + msg395512
2021-06-08 20:21:48joperezcreate