python / python/mypy

User defined type-level operators works, are they legal?

Offen
#13,894 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

documentation
Vorherrschende Sprache
Python
Sterne
20.6k
Forks
3.3k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

While looking for a workaround about the lack of extensible typed records, found some approaches that works, but then I realized, I'm using quite a few type level operators for that, are they legal?

My use case is too cumbersome and irrelevant to explain, but a simpler self-explanatory example:

Retrieving inner type of Awaitable:
from typing import Awaitable, Type, TypeVar

A = TypeVar("A")


class typeofMeta(type):
    def __getitem__(cls, value: A) -> Type[A]: raise NotImplementedError
class AwaitedMeta(type):
    def __getitem__(cls, value: Type[Awaitable[A]]) -> Type[A]: raise NotImplementedError

class typeof(metaclass=typeofMeta): ...
class Awaited(metaclass=AwaitedMeta): ...


async def add(a: int, b: int) -> int:
    return a + b


coro = add(1, 2)

reveal_type(Awaited[typeof[coro]])
# Revealed type is "Type[builtins.int]"

By defining a __getitem__ at class level I can do whatever with the types and make native-looking type operators. After that I noticed the use of @_SpecialForm in typing internals, but no mention that I could find in docs if this use can be replicated by developers. So I'm wondering if it's safe to rely on this kind of stuff, it enables a lot of nice composition patterns and zero-cost abstractions structures, like is common is TS.

Here are a few more typescript util clones, full implementation in gist:

Type-Level if-else
reveal_type(InstanceType[If[True, int, str]])
# Revealed type is 'builtins.int'

reveal_type(InstanceType[If[False, int, str]])
# Revealed type is 'builtins.str'
Get precise type of an object
def add(a: int, b: int) -> int: return a + b

reveal_type(typeof[add])
# Revealed type is 'def (builtins.int, builtins.int) -> builtins.int'

reveal_type(Type[add])  # with builtin `Type` get down to object
# Revealed type is builtins.object'
Get instance type from a class
class Box(Generic[A]):
    value: A

reveal_type(InstanceType[Box[int]])
# Revealed type is 'Box[builtins.int]'
Get return type of a callable
def add(a: int, b: int) -> int: return a + b

reveal_type(ReturnType[add])
# Revealed type is 'builtins.int'
Get parameter typle of a callable
def add(a: int, b: int) -> int: return a + b

reveal_type(Parameters[add])
# Revealed type is 'Tuple[builtins.int, builtins.int]'
Get inner type of an Awaitable
async def add(a: int, b: int) -> int: return a + b
coro = add(1, 2)

reveal_type(Awaited[typeof[coro]])
# Revealed type is "Type[builtins.int]"
Get item type of an iterable
reveal_type(ItemOf[list[int]])
# Revealed type is 'Type[builtins.int]'
Get key type of a Mapping
reveal_type(keyof[dict[str, int]])
# Revealed type is 'builtins.str'

So, are those legal?

Searched but couldn't find docs on it. I do remember seeing @gvanrossum commenting on some issue (couldn't find it now) that mypy is being conservative about allowing user-defined type operators, so I'm a bit afraid of using those.

In pyright it's even crazier, a lot of tricks with tuples are possible with Head[Unpack[Vars]] and Tail[Unpack[Vars]], plus some typevar bounding behaviour that made me notice even higher kinded types implementation is possible using the Lightweight higher-kinded polymorphism encoding, or maybe even a small type-level implementation of lisp

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginne mit der Überprüfung der benutzerdefinierten Operatoren und der reveal_type-Beispiele im Issue und vergleiche sie anschließend mit mypy/typeshed/stdlib/typing.pyi und dem verlinkten pyright-Verhalten. Ermittle, ob die dokumentierten Typing-Regeln diese Konstrukte abdecken und welche konkrete Dokumentationslücke verbleibt; das Issue ist erst abgeschlossen, wenn die Zulässigkeit und die unterstützte Verwendung dokumentiert oder eindeutig geklärt sind.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
devtools
Issue-Typ
Dokumentation
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Veraltet
Klarheit
Muss geklärt werden
Anfängerfreundlichkeit
25/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.