-
Notifications
You must be signed in to change notification settings - Fork 237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create PEP for Map with bind: match Tuple of parameterized types with parameters from TypeVarTuple #1383
Comments
There are a couple of problems with your proposed solution. First, keyword arguments are not allowed syntactically within an index expression. Support for this was proposed but unfortunately rejected in PEP 637. Second, you've proposed associating the "bound" parameter with a reference to a Scoping of TypeVars is admittedly confusing and poorly understood by many Python users. This is partly why we are pursuing PEP 695. |
I am not committed to that syntax. Mostly I would just like some kind of operation like Map (or similar) to allow expressing element-wise relationships between types in two related variadic tuples. The "bind=" here is because in this case
It would refer unambiguously to the For example, the draft of PEP-646 would allow But the draft of PEP-646 would not allow
This looks interesting. I also understand the desire to avoid creating a Turing-complete / unsolvable type system, which limits what programmatic typing features can be safely added. Map would obviously add to the complexity somewhat, but I don't think it poses too much of a risk. To give more philosophical motivation: it is already possible to type the example I gave above by something like: class ConverterCollection:
def __init__(self, detectors: list[Detector], converters: list[Converter]):
self.detectors = detectors
self.converters = converters But now you have not constrained there to be the same number of detectors and converters, and you have not constrained the types for each detector to match with the type of its corresponding converter. I am really not dogmatic about the exact solution, but the general idea is that you would want to be able to impose constraints on variadic generic types in some kind of simple language (maybe a relational language). The role of "Map" is just to impose the above constraints on the types of detectors and converters. It could be filled by any other way of expressing that these two variadic generic types are to have the same length and such-and-such parameters should be related in such-and-such way. Another way of doing this might be to have an explicit language for type constraints, for example a syntax like this: Ts = TypeVarTuple('Ts')
Detectors = TypeVarTuple('Detectors', constraint='Detectors[i] = Detector[Ts[i]]')
Converters = TypeVarTuple('Converters', constraint='Converters[i] = Converter[Ts[i], S]') A generic type like T = TypeVar('T')
S = TypeVar('S', constraint='S=T')
SelfMapping = Mapping[T, S] So the idea is just to allow more sophisticated constraints than is allowed by coreference to the same TypeVar. I'm not proposing this (it is very ugly and has problems), just trying to illustrate that the typing feature I am interested in follows a kind of "multiply, then constrain" paradigm, where the two main operations for constructing new generic types are to make multiple new generic types (which are by default totally independent and unrelated), and then constrain those generic types by imposing relations on them. I believe this kind of paradigm is unlikely to create an unsolvable type system, as long as the constraints can be computed. I view To focus the discussion (since I do not want to try to argue for a syntax change that has already been rejected):
For the record, I don't have any great knowledge of type systems, nor do I have strong opinions on the right solution here. I opened this issue mostly to follow up with the seemingly forgotten promise of a future PEP for Map. |
If individuals like you feel like there's a compelling need for |
A motivating use case, without full detail here, if it helps to have simple motivating examples supporting this down the line. class SerializationWrapper[*Ts]:
def __init__(
self, types: Map[Type, *Ts],
additional_encoding_hooks: Callable | None = None,
additional_decoding_hooks: Callable | None = None,
):
self._types: Map[Type, *Ts] = types
# create stateful en/decoders, register these types, and raise if the types aren't understood or
# if the types have ambiguous overlap from the perspective of encoding or decoding
# after the registration of optional hooks
def encode(obj: Union[*Ts]) -> bytes:
...
def decode(bytes) -> Union[*Ts]:
... |
I would suggest using an unpacked TypeVarTuple with subsequent unpacking GenericAlias (or special class for this PackedGenericAlias): class SerializationWrapper[*Ts]:
def __init__(
self, types: *type[Ts],
additional_encoding_hooks: Callable | None = None,
additional_decoding_hooks: Callable | None = None,
):
self._types: *type[Ts] = types
# create stateful en/decoders, register these types, and raise if the types aren't understood or
# if the types have ambiguous overlap from the perspective of encoding or decoding
# after the registration of optional hooks
def encode(obj: *type[Ts]) -> bytes:
...
def decode(bytes) -> Union[*Ts]:
... |
It was sad to see Map operation removed from PEP 646 (but understandable). |
Prior work in PEP-646 on Map was dropped to simplify it, with a promise it would appear in a future PEP. Here is a reference to the dropped Map feature: #193 (comment)
Here is an discussion of a potential usage of this Map feature for matching argument types for a length-n Tuple of unary Callables with a length-n Tuple parameterized by a TypeVarTuple: https://groups.google.com/g/dev-python/c/SbPOxIEvI60?pli=1
Here is a draft spec of PEP-646 that included discussion of Map: https://github.com/python/peps/blob/bf897f8c839d1b4d4534ab1fa223a210e2cacf06/pep-0646.rst
I would like to propose an extension of Map with a "bind" argument to allow disambiguation in case the parameterized type has multiple generic parameters. To give an example:
The
bind=Domain
argument here would be because Converter class is generic in both Domain and Codomain, so there is ambiguity about which generic parameter to Map. This is not necessary for Detector because, because it is generic only over Domain, so there is no ambiguity what to map over.The text was updated successfully, but these errors were encountered: